7 changed files with 1023 additions and 31 deletions
@ -0,0 +1,55 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> |
|||
<PropertyGroup> |
|||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
|||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
|||
<ProjectGuid>{BAF8082B-8DD8-4E7C-9C5B-D9213E08C3B1}</ProjectGuid> |
|||
<OutputType>Library</OutputType> |
|||
<AppDesignerFolder>Properties</AppDesignerFolder> |
|||
<RootNamespace>OmronPlcDriver</RootNamespace> |
|||
<AssemblyName>OmronPlcDriver</AssemblyName> |
|||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion> |
|||
<FileAlignment>512</FileAlignment> |
|||
<TargetFrameworkProfile>Client</TargetFrameworkProfile> |
|||
</PropertyGroup> |
|||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
|||
<DebugSymbols>true</DebugSymbols> |
|||
<DebugType>full</DebugType> |
|||
<Optimize>false</Optimize> |
|||
<OutputPath>bin\Debug\</OutputPath> |
|||
<DefineConstants>DEBUG;TRACE</DefineConstants> |
|||
<ErrorReport>prompt</ErrorReport> |
|||
<WarningLevel>4</WarningLevel> |
|||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
|||
</PropertyGroup> |
|||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
|||
<DebugType>pdbonly</DebugType> |
|||
<Optimize>true</Optimize> |
|||
<OutputPath>bin\Release\</OutputPath> |
|||
<DefineConstants>TRACE</DefineConstants> |
|||
<ErrorReport>prompt</ErrorReport> |
|||
<WarningLevel>4</WarningLevel> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<Reference Include="System" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<Compile Include="OmronUdpReader.cs" /> |
|||
<Compile Include="Properties\AssemblyInfo.cs" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<ProjectReference Include="..\DataService\DataService.csproj"> |
|||
<Project>{8965e389-6466-4b30-bd43-83c909044637}</Project> |
|||
<Name>DataService</Name> |
|||
</ProjectReference> |
|||
</ItemGroup> |
|||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> |
|||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. |
|||
Other similar extension points exist, see Microsoft.Common.targets. |
|||
<Target Name="BeforeBuild"> |
|||
</Target> |
|||
<Target Name="AfterBuild"> |
|||
</Target> |
|||
--> |
|||
</Project> |
|||
@ -0,0 +1,875 @@ |
|||
using DataService; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using System.Net; |
|||
using System.Net.Sockets; |
|||
using System.Text; |
|||
using System.Timers; |
|||
|
|||
namespace OmronPlcDriver |
|||
{ |
|||
[Description("Omron(CS/CJ) UDP协议")] |
|||
public sealed class OmronUdpReader : IPLCDriver, IMultiReadWrite |
|||
{ |
|||
#region
|
|||
|
|||
/****************************************/ |
|||
//更新人:新增_pdu,可以根据实际情况进行采集
|
|||
//更新日期:20171205
|
|||
//更新原因:根据现场进行参数调整以提供采集响应速度
|
|||
/***************************************/ |
|||
/// <summary>
|
|||
/// PDU的值
|
|||
/// </summary>
|
|||
int _pdu; |
|||
/// <summary>
|
|||
/// 获取PDU的值
|
|||
/// </summary>
|
|||
public int PDU |
|||
{ |
|||
get { return _pdu; } |
|||
} |
|||
/// <summary>
|
|||
/// 获取设备地址
|
|||
/// </summary>
|
|||
/// <param name="address"></param>
|
|||
/// <returns></returns>
|
|||
public DeviceAddress GetDeviceAddress(string address) |
|||
{ |
|||
DeviceAddress dv = DeviceAddress.Empty; |
|||
if (string.IsNullOrEmpty(address)) |
|||
return dv; |
|||
dv.Area = _plcNodeId; |
|||
switch (address[0]) |
|||
{ |
|||
case '0': |
|||
case '1': |
|||
case '2': |
|||
case '3': |
|||
case '4': |
|||
case '5': |
|||
case '6': |
|||
case '7': |
|||
case '8': |
|||
case '9': |
|||
{ |
|||
int index = address.IndexOf('.'); |
|||
dv.DBNumber = OmronCSCJ.fctCIO; |
|||
if (index > 0) |
|||
{ |
|||
dv.Start = int.Parse(address.Substring(0, index - 1)); |
|||
dv.Bit = byte.Parse(address.Substring(index + 1)); |
|||
} |
|||
else |
|||
dv.Start = int.Parse(address); |
|||
} |
|||
break; |
|||
case 'D'://DM区
|
|||
{ |
|||
int index = address.IndexOf('.'); |
|||
dv.DBNumber = OmronCSCJ.fctDM; |
|||
if (index > 0) |
|||
{ |
|||
dv.Start = int.Parse(address.Substring(1, index - 1)); |
|||
dv.Bit = byte.Parse(address.Substring(index + 1)); |
|||
} |
|||
else |
|||
dv.Start = int.Parse(address.Substring(index + 1)); |
|||
} |
|||
break; |
|||
case 'H'://HR区
|
|||
{ |
|||
int index = address.IndexOf('.'); |
|||
dv.DBNumber = OmronCSCJ.fctHR; |
|||
if (index > 0) |
|||
{ |
|||
dv.Start = int.Parse(address.Substring(1, index - 1)); |
|||
dv.Bit = byte.Parse(address.Substring(index + 1)); |
|||
} |
|||
else |
|||
dv.Start = int.Parse(address.Substring(1)); |
|||
} |
|||
break; |
|||
case 'A'://AR区
|
|||
{ |
|||
int index = address.IndexOf('.'); |
|||
dv.DBNumber = OmronCSCJ.fctA; |
|||
if (index > 0) |
|||
{ |
|||
dv.Start = int.Parse(address.Substring(1, index - 1)); |
|||
dv.Bit = byte.Parse(address.Substring(index + 1)); |
|||
} |
|||
else |
|||
dv.Start = int.Parse(address.Substring(1)); |
|||
} |
|||
break; |
|||
} |
|||
return dv; |
|||
} |
|||
|
|||
public string GetAddress(DeviceAddress address) |
|||
{ |
|||
return string.Empty; |
|||
} |
|||
|
|||
#endregion
|
|||
private int _timeout;//超时数据
|
|||
|
|||
private Socket udpSynCl; |
|||
private byte[] udpSynClBuffer = new byte[1024]; |
|||
|
|||
short _id;//驱动id
|
|||
//驱动id
|
|||
public short ID |
|||
{ |
|||
get |
|||
{ |
|||
return _id; |
|||
} |
|||
} |
|||
|
|||
string _name;//驱动名称
|
|||
/// <summary>
|
|||
/// 驱动名称
|
|||
/// </summary>
|
|||
public string Name |
|||
{ |
|||
get |
|||
{ |
|||
return _name; |
|||
} |
|||
} |
|||
|
|||
string _ip;//服务ip
|
|||
int _port = 9600; //服务端口
|
|||
public string ServerName |
|||
{ |
|||
get { return _ip + ":" + _port; } |
|||
set |
|||
{ |
|||
if (!string.IsNullOrEmpty(value)) |
|||
{ |
|||
int index = value.IndexOf(':'); |
|||
_ip = value.Substring(0, index - 1);//ip地址
|
|||
_port = int.Parse(value.Substring(index + 1));//端口号
|
|||
} |
|||
} |
|||
} |
|||
/// <summary>
|
|||
/// 是否关闭
|
|||
/// </summary>
|
|||
public bool IsClosed |
|||
{ |
|||
get |
|||
{ |
|||
return udpSynCl == null || udpSynCl.Connected == false; |
|||
} |
|||
} |
|||
/// <summary>
|
|||
/// 超时时间
|
|||
/// </summary>
|
|||
|
|||
public int TimeOut |
|||
{ |
|||
get { return _timeout; } |
|||
set { _timeout = value; } |
|||
} |
|||
|
|||
byte _plcNodeId;//plc节点号
|
|||
/// <summary>
|
|||
/// plc节点号,从para1参数读取
|
|||
/// </summary>
|
|||
public byte PlcNodeId |
|||
{ |
|||
get { return _pcNodeId; } |
|||
set { _pcNodeId = value; } |
|||
} |
|||
|
|||
byte _pcNodeId;//电脑节点号
|
|||
/// <summary>
|
|||
/// 电脑节点号,从para2参数读取
|
|||
/// </summary>
|
|||
public byte PcNodeId |
|||
{ |
|||
get { return _pcNodeId; } |
|||
set { _pcNodeId = value; } |
|||
} |
|||
|
|||
List<IGroup> _grps = new List<IGroup>(20); |
|||
public IEnumerable<IGroup> Groups |
|||
{ |
|||
get { return _grps; } |
|||
} |
|||
|
|||
IDataServer _server; |
|||
public IDataServer Parent |
|||
{ |
|||
get { return _server; } |
|||
} |
|||
|
|||
public OmronUdpReader(IDataServer server, short id, string name, string servername, int timeOut = 500, string spare1 = null, string spare2 = null) |
|||
{ |
|||
_id = id; |
|||
_name = name; |
|||
_server = server; |
|||
if (!string.IsNullOrEmpty(servername)) |
|||
{ |
|||
int index = servername.IndexOf(':'); |
|||
_ip = servername.Substring(0, index - 1);//ip地址
|
|||
_port = int.Parse(servername.Substring(index + 1));//端口号
|
|||
} |
|||
_timeout = timeOut; |
|||
byte.TryParse(spare1, out _plcNodeId); |
|||
byte.TryParse(spare2, out _pcNodeId); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 连接
|
|||
/// </summary>
|
|||
/// <returns></returns>
|
|||
public bool Connect() |
|||
{ |
|||
try |
|||
{ |
|||
if (udpSynCl != null) |
|||
udpSynCl.Close(); |
|||
//IPAddress ip = IPAddress.Parse(_ip);
|
|||
// ----------------------------------------------------------------
|
|||
// Connect synchronous client
|
|||
udpSynCl = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Udp); |
|||
udpSynCl.SendTimeout = _timeout; |
|||
udpSynCl.ReceiveTimeout = _timeout; |
|||
udpSynCl.NoDelay = true; |
|||
udpSynCl.Connect(_ip, _port); |
|||
return true; |
|||
} |
|||
catch (SocketException error) |
|||
{ |
|||
if (OnClose != null) |
|||
OnClose(this, new ShutdownRequestEventArgs(error.Message)); |
|||
return false; |
|||
} |
|||
} |
|||
/// <summary>
|
|||
/// 生成读命令头
|
|||
/// </summary>
|
|||
/// <param name="pcnode">电脑节点号,设置和PLC节点不一致即可</param>
|
|||
/// <param name="startAddress">读取的起始地址</param>
|
|||
/// <param name="length">读取长度</param>
|
|||
/// <param name="function"></param>
|
|||
/// <param name="plcnode">PLC节点号,可为0</param>
|
|||
/// <returns></returns>
|
|||
private byte[] CreateReadHeader(byte pcnode, int startAddress, ushort length, byte function, byte plcnode = 0) |
|||
{ |
|||
byte[] data = new byte[18]; |
|||
data[0] = 0x80; |
|||
data[1] = 0; |
|||
data[2] = 0; //80 00 02 固定帧头
|
|||
data[2] = 0; |
|||
data[3] = plcnode; |
|||
data[5] = 0; //设备的网络号,节点号,单元号
|
|||
data[6] = 0; |
|||
data[7] = pcnode; |
|||
data[8] = 0; //PC的网络号,节点号,单元号
|
|||
data[9] = 0; |
|||
data[10] = 1; |
|||
data[11] = 1; //SID+MRC+SRC
|
|||
data[12] = function; //数据区代码
|
|||
byte[] _adr = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)startAddress)); |
|||
data[13] = _adr[0]; // 首地址高字节
|
|||
data[14] = _adr[1]; // 首地址低字节
|
|||
data[15] = 0; // 固定0
|
|||
byte[] _length = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)length)); |
|||
data[16] = _length[0]; // 读取数量高字节
|
|||
data[17] = _length[1]; // 读取数量低字节
|
|||
return data; |
|||
} |
|||
/// <summary>
|
|||
/// 生成写命令头
|
|||
/// </summary>
|
|||
/// <param name="id"></param>
|
|||
/// <param name="startAddress"></param>
|
|||
/// <param name="numData"></param>
|
|||
/// <param name="length"></param>
|
|||
/// <param name="function"></param>
|
|||
/// <returns></returns>
|
|||
private byte[] CreateWriteHeader(byte pcnode, int startAddress, ushort numData, byte function, byte plcnode = 0) |
|||
{ |
|||
byte[] data = new byte[numData + 18]; |
|||
data[0] = 0x80; |
|||
data[1] = 0; |
|||
data[2] = 0; //80 00 02 固定帧头
|
|||
data[3] = 0; |
|||
data[4] = plcnode; |
|||
data[5] = 0; //设备的网络号,节点号,单元号
|
|||
data[6] = 0; |
|||
data[7] = pcnode; |
|||
data[8] = 0; //PC的网络号,节点号,单元号
|
|||
data[9] = 0; |
|||
data[10] = 1; |
|||
data[11] = 2; //SID+MRC+SRC
|
|||
data[12] = function; //数据区代码
|
|||
byte[] _adr = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)startAddress)); |
|||
data[13] = _adr[0]; // 首地址高字节
|
|||
data[14] = _adr[1]; // 首地址低字节
|
|||
data[15] = 0; // 固定0
|
|||
byte[] length = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)numData / 2)); |
|||
data[16] = length[0]; // 写取数量高字节
|
|||
data[17] = length[1]; // 写取数量低字节
|
|||
return data; |
|||
} |
|||
/// <summary>
|
|||
/// 同步写数据到udp
|
|||
/// </summary>
|
|||
/// <param name="write_data">写的字节数组</param>
|
|||
/// <returns></returns>
|
|||
private byte[] WriteSyncData(byte[] write_data) |
|||
{ |
|||
short id = BitConverter.ToInt16(write_data, 4); |
|||
if (IsClosed) CallException(id, write_data[12], OmronCSCJ.excExceptionConnectionLost); |
|||
else |
|||
{ |
|||
try |
|||
{ |
|||
udpSynCl.Send(write_data, 0, write_data.Length, SocketFlags.None);//是否存在lock的问题?
|
|||
int result = udpSynCl.Receive(udpSynClBuffer, 0, 1024, SocketFlags.None); |
|||
|
|||
byte function = udpSynClBuffer[11];//读写功能
|
|||
byte[] data; |
|||
|
|||
int err = udpSynClBuffer[12] * 256 + udpSynClBuffer[13]; |
|||
|
|||
if (result == 0) CallException(id, write_data[12], OmronCSCJ.excExceptionConnectionLost); |
|||
|
|||
// ------------------------------------------------------------
|
|||
// Response data is slave ModbusModbus.exception
|
|||
if (err != 0) |
|||
{ |
|||
CallException(id, function, 4); |
|||
return null; |
|||
} |
|||
// ------------------------------------------------------------
|
|||
// Write response data
|
|||
else if (function == 0x2) |
|||
{ |
|||
data = new byte[2]; |
|||
Array.Copy(udpSynClBuffer, 10, data, 0, 2); |
|||
} |
|||
// ------------------------------------------------------------
|
|||
// Read response data
|
|||
else |
|||
{ |
|||
data = new byte[(write_data[16] * 256 + write_data[17]) * 2]; |
|||
Array.Copy(udpSynClBuffer, 14, data, 0, data.Length); |
|||
} |
|||
return data; |
|||
} |
|||
catch (SocketException) |
|||
{ |
|||
CallException(id, write_data[12], OmronCSCJ.excExceptionConnectionLost); |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 写单个寄存器
|
|||
/// </summary>
|
|||
/// <param name="pcnode">电脑节点号</param>
|
|||
/// <param name="startAddress">起始地址</param>
|
|||
/// <param name="function">内存区功能吗</param>
|
|||
/// <param name="values">写值数组</param>
|
|||
/// <param name="plcnode">plc节点号</param>
|
|||
/// <returns></returns>
|
|||
public byte[] WriteSingleRegister(byte pcnode, int startAddress, byte function, byte[] values, byte plcnode = 0) |
|||
{ |
|||
byte[] data; |
|||
data = CreateWriteHeader(pcnode, startAddress, 2, function, plcnode); |
|||
data[19] = values[0]; |
|||
data[20] = values[1]; |
|||
return WriteSyncData(data); |
|||
} |
|||
/// <summary>
|
|||
/// 写多个寄存器
|
|||
/// </summary>
|
|||
/// <param name="pcnode">电脑节点号</param>
|
|||
/// <param name="startAddress">起始地址</param>
|
|||
/// <param name="function">内存区功能号</param>
|
|||
/// <param name="values">写值数组</param>
|
|||
/// <param name="plcnode">plc节点号</param>
|
|||
/// <returns></returns>
|
|||
public byte[] WriteMultipleRegister(byte pcnode, int startAddress, byte function, byte[] values, byte plcnode = 0) |
|||
{ |
|||
ushort numBytes = Convert.ToUInt16(values.Length); |
|||
if (numBytes % 2 > 0) numBytes++; |
|||
byte[] data; |
|||
|
|||
data = CreateWriteHeader(pcnode, startAddress, numBytes, function, plcnode); |
|||
Array.Copy(values, 0, data, 19, values.Length); |
|||
return WriteSyncData(data); |
|||
} |
|||
|
|||
public IGroup AddGroup(string name, short id, int updateRate, float deadBand = 0f, bool active = false) |
|||
{ |
|||
OmronUdpGroup grp = new OmronUdpGroup(id, name, updateRate, active, this); |
|||
_grps.Add(grp); |
|||
return grp; |
|||
} |
|||
|
|||
public bool RemoveGroup(IGroup grp) |
|||
{ |
|||
grp.IsActive = false; |
|||
return _grps.Remove(grp); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
if (udpSynCl != null) |
|||
{ |
|||
try { udpSynCl.Shutdown(SocketShutdown.Both); } |
|||
catch { } |
|||
udpSynCl.Close(); |
|||
|
|||
udpSynCl = null; |
|||
} |
|||
foreach (IGroup grp in _grps) |
|||
{ |
|||
grp.Dispose(); |
|||
} |
|||
_grps.Clear(); |
|||
} |
|||
|
|||
internal string GetErrorString(byte exception) |
|||
{ |
|||
switch (exception) |
|||
{ |
|||
case OmronCSCJ.excIllegalFunction: |
|||
return "Constant for OmronCSCJ.exception illegal function."; |
|||
case OmronCSCJ.excIllegalDataAdr: |
|||
return "Constant for OmronCSCJ.exception illegal data address."; |
|||
case OmronCSCJ.excIllegalDataVal: |
|||
return "Constant for OmronCSCJ.exception illegal data value."; |
|||
case OmronCSCJ.excSlaveDeviceFailure: |
|||
return "Constant for OmronCSCJ.exception slave device failure."; |
|||
case OmronCSCJ.excAck: |
|||
return "Constant for OmronCSCJ.exception acknowledge."; |
|||
case OmronCSCJ.excSlaveIsBusy: |
|||
return "Constant for OmronCSCJ.exception slave is busy/booting up."; |
|||
case OmronCSCJ.excGatePathUnavailable: |
|||
return "Constant for OmronCSCJ.exception gate path unavailable."; |
|||
case OmronCSCJ.excExceptionNotConnected: |
|||
return "Constant for OmronCSCJ.exception not connected."; |
|||
case OmronCSCJ.excExceptionConnectionLost: |
|||
return "Constant for OmronCSCJ.exception connection lost."; |
|||
case OmronCSCJ.excExceptionTimeout: |
|||
return "Constant for OmronCSCJ.exception response timeout."; |
|||
case OmronCSCJ.excExceptionOffset: |
|||
return "Constant for OmronCSCJ.exception wrong offset."; |
|||
case OmronCSCJ.excSendFailt: |
|||
return "Constant for OmronCSCJ.exception send failt."; |
|||
} |
|||
return string.Empty; |
|||
} |
|||
|
|||
internal void CallException(int id, byte function, byte exception) |
|||
{ |
|||
if (udpSynCl == null) return; |
|||
if (exception == OmronCSCJ.excExceptionConnectionLost && IsClosed == false) |
|||
{ |
|||
if (OnClose != null) |
|||
OnClose(this, new ShutdownRequestEventArgs(GetErrorString(exception))); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 读取字节数组
|
|||
/// </summary>
|
|||
/// <param name="address">标签变量地址结构</param>
|
|||
/// <param name="size">长度</param>
|
|||
/// <returns></returns>
|
|||
public byte[] ReadBytes(DeviceAddress address, ushort size) |
|||
{ |
|||
ushort len = size; |
|||
if (len % 2 != 0) |
|||
{ |
|||
len++; |
|||
} |
|||
return WriteSyncData(CreateReadHeader(PcNodeId, address.Start, (ushort)(len / 2), (byte)address.DBNumber, (byte)address.Area)); |
|||
} |
|||
/// <summary>
|
|||
/// 读取32位整数
|
|||
/// </summary>
|
|||
/// <param name="address">标签变量地址结构</param>
|
|||
/// <returns></returns>
|
|||
public ItemData<int> ReadInt32(DeviceAddress address) |
|||
{ |
|||
byte[] data = WriteSyncData(CreateReadHeader(PcNodeId, address.Start, 2, (byte)address.DBNumber, (byte)address.Area)); |
|||
if (data == null) |
|||
return new ItemData<int>(0, 0, QUALITIES.QUALITY_BAD); |
|||
else |
|||
return new ItemData<int>(IPAddress.HostToNetworkOrder(BitConverter.ToInt32(data, 0)), 0, QUALITIES.QUALITY_GOOD); |
|||
} |
|||
/// <summary>
|
|||
/// 读取16位整数
|
|||
/// </summary>
|
|||
/// <param name="address">标签变量地址结构</param>
|
|||
/// <returns></returns>
|
|||
public ItemData<short> ReadInt16(DeviceAddress address) |
|||
{ |
|||
byte[] data = WriteSyncData(CreateReadHeader(PcNodeId, address.Start, 1, (byte)address.DBNumber, (byte)address.Area)); |
|||
if (data == null) |
|||
return new ItemData<short>(0, 0, QUALITIES.QUALITY_BAD); |
|||
else |
|||
return new ItemData<short>(IPAddress.HostToNetworkOrder(BitConverter.ToInt16(data, 0)), 0, QUALITIES.QUALITY_GOOD); |
|||
} |
|||
/// <summary>
|
|||
/// 读取1字节
|
|||
/// </summary>
|
|||
/// <param name="address">标签变量地址结构</param>
|
|||
/// <returns></returns>
|
|||
public ItemData<byte> ReadByte(DeviceAddress address) |
|||
{ |
|||
byte[] data = WriteSyncData(CreateReadHeader(PcNodeId, address.Start, 1, (byte)address.DBNumber, (byte)address.Area)); |
|||
if (data == null) |
|||
return new ItemData<byte>(0, 0, QUALITIES.QUALITY_BAD); |
|||
else |
|||
return new ItemData<byte>(data[0], 0, QUALITIES.QUALITY_GOOD); |
|||
} |
|||
/// <summary>
|
|||
/// 读取字符串
|
|||
/// </summary>
|
|||
/// <param name="address">标签变量地址结构</param>
|
|||
/// <param name="size">长度</param>
|
|||
/// <returns></returns>
|
|||
public ItemData<string> ReadString(DeviceAddress address, ushort size) |
|||
{ |
|||
byte[] data = WriteSyncData(CreateReadHeader(PcNodeId, address.Start, size, (byte)address.DBNumber, (byte)address.Area)); |
|||
if (data == null) |
|||
return new ItemData<string>(string.Empty, 0, QUALITIES.QUALITY_BAD); |
|||
else |
|||
return new ItemData<string>(Encoding.ASCII.GetString(data, 0, data.Length), 0, QUALITIES.QUALITY_GOOD);//是否考虑字节序问题?
|
|||
} |
|||
/// <summary>
|
|||
/// 读取32位浮点数
|
|||
/// </summary>
|
|||
/// <param name="address">标签变量地址结构</param>
|
|||
/// <returns></returns>
|
|||
public unsafe ItemData<float> ReadFloat(DeviceAddress address) |
|||
{ |
|||
byte[] data = WriteSyncData(CreateReadHeader(PcNodeId, address.Start, 2, (byte)address.DBNumber, (byte)address.Area)); |
|||
if (data == null) |
|||
return new ItemData<float>(0.0f, 0, QUALITIES.QUALITY_BAD); |
|||
else |
|||
{ |
|||
int value = IPAddress.HostToNetworkOrder(BitConverter.ToInt32(data, 0)); |
|||
return new ItemData<float>(*(((float*)&value)), 0, QUALITIES.QUALITY_GOOD); |
|||
} |
|||
} |
|||
/// <summary>
|
|||
/// 读取1位
|
|||
/// </summary>
|
|||
/// <param name="address">标签变量地址结构体</param>
|
|||
/// <returns></returns>
|
|||
public ItemData<bool> ReadBit(DeviceAddress address) |
|||
{ |
|||
byte[] data = WriteSyncData(CreateReadHeader(PcNodeId, address.Start, 1, (byte)address.DBNumber, (byte)address.Area)); |
|||
if (data == null) |
|||
return new ItemData<bool>(false, 0, QUALITIES.QUALITY_BAD); |
|||
if (data.Length == 1) return new ItemData<bool>(data[0] > 0, 0, QUALITIES.QUALITY_GOOD); |
|||
unsafe |
|||
{ |
|||
fixed (byte* p = data) |
|||
{ |
|||
short* p1 = (short*)p; |
|||
return new ItemData<bool>((*p1 & (1 << address.Bit.BitSwap())) |
|||
!= 0, 0, QUALITIES.QUALITY_GOOD); |
|||
} |
|||
} |
|||
} |
|||
/// <summary>
|
|||
/// 读object类型
|
|||
/// </summary>
|
|||
/// <param name="address"></param>
|
|||
/// <returns></returns>
|
|||
public ItemData<object> ReadValue(DeviceAddress address) |
|||
{ |
|||
return this.ReadValueEx(address); |
|||
} |
|||
/// <summary>
|
|||
/// 写字节数组到设备
|
|||
/// </summary>
|
|||
/// <param name="address">标签变量地址结构</param>
|
|||
/// <param name="bits">需写的字节数组</param>
|
|||
/// <returns></returns>
|
|||
public int WriteBytes(DeviceAddress address, byte[] bits) |
|||
{ |
|||
var data = WriteMultipleRegister(PcNodeId, address.Start, (byte)address.DBNumber, bits, (byte)address.Area); |
|||
return data == null ? -1 : 0; |
|||
} |
|||
|
|||
public int WriteBit(DeviceAddress address, bool bit) |
|||
{ |
|||
ItemData<short> item = ReadInt16(address); |
|||
short value = (short)((bit ? 1 : 0) << (int)address.Bit); |
|||
short result = (short)(item.Value | value); |
|||
var data = WriteSingleRegister(PcNodeId, address.Start, (byte)address.DBNumber, BitConverter.GetBytes(result), (byte)address.Area); |
|||
return data == null ? -1 : 0; |
|||
} |
|||
|
|||
public int WriteBits(DeviceAddress address, byte bits) |
|||
{ |
|||
var data = WriteSingleRegister(PcNodeId, address.Start, (byte)address.DBNumber, new byte[] { bits }, (byte)address.Area); |
|||
return data == null ? -1 : 0; |
|||
} |
|||
|
|||
public int WriteInt16(DeviceAddress address, short value) |
|||
{ |
|||
var data = WriteSingleRegister(PcNodeId, address.Start, (byte)address.DBNumber, BitConverter.GetBytes(value), (byte)address.Area); |
|||
return data == null ? -1 : 0; |
|||
} |
|||
|
|||
public int WriteInt32(DeviceAddress address, int value) |
|||
{ |
|||
var data = WriteMultipleRegister(PcNodeId, address.Start, (byte)address.DBNumber, BitConverter.GetBytes(value), (byte)address.Area); |
|||
return data == null ? -1 : 0; |
|||
} |
|||
|
|||
public int WriteFloat(DeviceAddress address, float value) |
|||
{ |
|||
var data = WriteMultipleRegister(PcNodeId, address.Start, (byte)address.DBNumber, BitConverter.GetBytes(value), (byte)address.Area); |
|||
return data == null ? -1 : 0; |
|||
} |
|||
|
|||
public int WriteString(DeviceAddress address, string str) |
|||
{ |
|||
var data = WriteMultipleRegister(PcNodeId, address.Start, (byte)address.DBNumber, Encoding.ASCII.GetBytes(str), (byte)address.Area); |
|||
return data == null ? -1 : 0; |
|||
} |
|||
|
|||
public int WriteValue(DeviceAddress address, object value) |
|||
{ |
|||
return this.WriteValueEx(address, value); |
|||
} |
|||
|
|||
public event ShutdownRequestEventHandler OnClose; |
|||
|
|||
public int Limit |
|||
{ |
|||
get { return 60; } |
|||
} |
|||
|
|||
public ItemData<Storage>[] ReadMultiple(DeviceAddress[] addrsArr) |
|||
{ |
|||
return this.PLCReadMultiple(new NetShortCacheReader(), addrsArr); |
|||
} |
|||
|
|||
public int WriteMultiple(DeviceAddress[] addrArr, object[] buffer) |
|||
{ |
|||
return this.PLCWriteMultiple(new NetShortCacheReader(), addrArr, buffer, Limit); |
|||
} |
|||
} |
|||
|
|||
public sealed class OmronUdpGroup : PLCGroup |
|||
{ |
|||
public OmronUdpGroup(short id, string name, int updateRate, bool active, IPLCDriver plcReader) |
|||
{ |
|||
this._id = id; |
|||
this._name = name; |
|||
this._updateRate = updateRate; |
|||
this._isActive = active; |
|||
this._plcReader = plcReader; |
|||
this._server = _plcReader.Parent; |
|||
this._timer = new Timer(); |
|||
this._changedList = new List<int>(); |
|||
this._cacheReader = new NetShortCacheReader(); |
|||
} |
|||
|
|||
protected override unsafe void Poll() |
|||
{ |
|||
short[] cache = (short[])_cacheReader.Cache; |
|||
int offset = 0; |
|||
foreach (PDUArea area in _rangeList) |
|||
{ |
|||
byte[] rcvBytes = _plcReader.ReadBytes(area.Start, (ushort)area.Len);//从PLC读取数据
|
|||
if (rcvBytes == null || rcvBytes.Length == 0) |
|||
{ |
|||
//_plcReader.Connect();
|
|||
continue; |
|||
} |
|||
else |
|||
{ |
|||
int len = rcvBytes.Length / 2; |
|||
fixed (byte* p1 = rcvBytes) |
|||
{ |
|||
short* prcv = (short*)p1; |
|||
int index = area.StartIndex;//index指向_items中的Tag元数据
|
|||
int count = index + area.Count; |
|||
while (index < count) |
|||
{ |
|||
DeviceAddress addr = _items[index].Address; |
|||
int iShort = addr.CacheIndex; |
|||
int iShort1 = iShort - offset; |
|||
if (addr.VarType == DataType.BOOL) |
|||
{ |
|||
int tmp = prcv[iShort1] ^ cache[iShort]; |
|||
DeviceAddress next = addr; |
|||
if (tmp != 0) |
|||
{ |
|||
while (addr.Start == next.Start) |
|||
{ |
|||
if ((tmp & (1 << next.Bit)) > 0) _changedList.Add(index); |
|||
if (++index < count) |
|||
next = _items[index].Address; |
|||
else |
|||
break; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
while (addr.Start == next.Start && ++index < count) |
|||
{ |
|||
next = _items[index].Address; |
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
if (addr.DataSize <= 2) |
|||
{ |
|||
if (prcv[iShort1] != cache[iShort]) _changedList.Add(index); |
|||
} |
|||
else |
|||
{ |
|||
int size = addr.DataSize / 2; |
|||
for (int i = 0; i < size; i++) |
|||
{ |
|||
if (prcv[iShort1 + i] != cache[iShort + i]) |
|||
{ |
|||
_changedList.Add(index); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
index++; |
|||
} |
|||
} |
|||
for (int j = 0; j < len; j++) |
|||
{ |
|||
cache[j + offset] = prcv[j]; |
|||
}//将PLC读取的数据写入到CacheReader中
|
|||
} |
|||
offset += len; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 欧姆龙Omron CS/CJ系列PLC数据区功能号和错误代码常数定义类
|
|||
/// </summary>
|
|||
public sealed class OmronCSCJ |
|||
{ |
|||
/*****************************************************/ |
|||
//OMRON PLC CS/CJ系列数据区功能号
|
|||
/****************************************************/ |
|||
/// <summary>
|
|||
/// CIO 0000 to CIO 6143
|
|||
/// </summary>
|
|||
public const byte fctCIO = 0xB0;//CIO 0000 to CIO 6143
|
|||
/// <summary>
|
|||
/// H000 to H511
|
|||
/// </summary>
|
|||
public const byte fctHR = 0xB2;//H000 to H511
|
|||
/// <summary>
|
|||
/// A448 to A959
|
|||
/// </summary>
|
|||
public const byte fctA = 0xB3;//A448 to A959
|
|||
/// <summary>
|
|||
/// //D00000 to D32767
|
|||
/// </summary>
|
|||
public const byte fctDM = 0x82;//D00000 to D32767
|
|||
/// <summary>
|
|||
/// E0_E00000 to E0_E32765
|
|||
/// </summary>
|
|||
public const byte fctE0 = 0xA0;//E0_E00000 to E0_E32765
|
|||
/// <summary>
|
|||
/// E1_E00000 to E1_E32765
|
|||
/// </summary>
|
|||
public const byte fctE1 = 0xA1;//E1_E00000 to E1_E32765
|
|||
/// <summary>
|
|||
/// E2_E00000 to E2_E32765
|
|||
/// </summary>
|
|||
public const byte fctE2 = 0xA2;//E2_E00000 to E2_E32765
|
|||
/// <summary>
|
|||
/// E3_E00000 to E3_E32765
|
|||
/// </summary>
|
|||
public const byte fctE3 = 0xA3; |
|||
/// <summary>
|
|||
/// E4_E00000 to E4_E32765
|
|||
/// </summary>
|
|||
public const byte fctE4 = 0xA4;//E4_E00000 to E4_E32765
|
|||
/// <summary>
|
|||
/// E5_E00000 to E5_E32765
|
|||
/// </summary>
|
|||
public const byte fctE5 = 0xA5;//E5_E00000 to E5_E32765
|
|||
/// <summary>
|
|||
/// E6_E00000 to E6_E32765
|
|||
/// </summary>
|
|||
public const byte fctE6 = 0xA6;//E6_E00000 to E6_E32765
|
|||
/// <summary>
|
|||
/// E7_E00000 to E7_E32765
|
|||
/// </summary>
|
|||
public const byte fctE7 = 0xA7;//E7_E00000 to E7_E32765
|
|||
/// <summary>
|
|||
/// E8_E00000 to E8_E32765
|
|||
/// </summary>
|
|||
public const byte fctE8 = 0xA8;//E8_E00000 to E8_E32765
|
|||
/// <summary>
|
|||
/// E9_E00000 to E9_E32765
|
|||
/// </summary>
|
|||
public const byte fctE9 = 0xA9;//E9_E00000 to E9_E32765
|
|||
/// <summary>
|
|||
/// EA_E00000 to EA_E32765
|
|||
/// </summary>
|
|||
public const byte fctEA = 0xAA;//EA_E00000 to EA_E32765
|
|||
/// <summary>
|
|||
/// EB_E00000 to EB_E32765
|
|||
/// </summary>
|
|||
public const byte fctEB = 0xAB;//EB_E00000 to EB_E32765
|
|||
/// <summary>
|
|||
/// EC_E00000 to EC_E32765
|
|||
/// </summary>
|
|||
public const byte fctEC = 0xAC;//EC_E00000 to EC_E32765
|
|||
/// <summary>
|
|||
/// E00000 to E32765
|
|||
/// </summary>
|
|||
public const byte fctCurrentbank = 98;//E00000 to E32765
|
|||
|
|||
|
|||
/// <summary>Constant for exception illegal function.</summary>
|
|||
public const byte excIllegalFunction = 1; |
|||
/// <summary>Constant for exception illegal data address.</summary>
|
|||
public const byte excIllegalDataAdr = 2; |
|||
/// <summary>Constant for exception illegal data value.</summary>
|
|||
public const byte excIllegalDataVal = 3; |
|||
/// <summary>Constant for exception slave device failure.</summary>
|
|||
public const byte excSlaveDeviceFailure = 4; |
|||
/// <summary>Constant for exception acknowledge.</summary>
|
|||
public const byte excAck = 5; |
|||
/// <summary>Constant for exception slave is busy/booting up.</summary>
|
|||
public const byte excSlaveIsBusy = 6; |
|||
/// <summary>Constant for exception gate path unavailable.</summary>
|
|||
public const byte excGatePathUnavailable = 10; |
|||
/// <summary>Constant for exception not connected.</summary>
|
|||
public const byte excExceptionNotConnected = 253; |
|||
/// <summary>Constant for exception connection lost.</summary>
|
|||
public const byte excExceptionConnectionLost = 254; |
|||
/// <summary>Constant for exception response timeout.</summary>
|
|||
public const byte excExceptionTimeout = 255; |
|||
/// <summary>Constant for exception wrong offset.</summary>
|
|||
public const byte excExceptionOffset = 128; |
|||
/// <summary>Constant for exception send failt.</summary>
|
|||
public const byte excSendFailt = 100; |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
using System.Reflection; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
// General Information about an assembly is controlled through the following
|
|||
// set of attributes. Change these attribute values to modify the information
|
|||
// associated with an assembly.
|
|||
[assembly: AssemblyTitle("OmronPlcDriver")] |
|||
[assembly: AssemblyDescription("")] |
|||
[assembly: AssemblyConfiguration("")] |
|||
[assembly: AssemblyCompany("Microsoft")] |
|||
[assembly: AssemblyProduct("OmronPlcDriver")] |
|||
[assembly: AssemblyCopyright("Copyright © Microsoft 2017")] |
|||
[assembly: AssemblyTrademark("")] |
|||
[assembly: AssemblyCulture("")] |
|||
|
|||
// Setting ComVisible to false makes the types in this assembly not visible
|
|||
// to COM components. If you need to access a type in this assembly from
|
|||
// COM, set the ComVisible attribute to true on that type.
|
|||
[assembly: ComVisible(false)] |
|||
|
|||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
|||
[assembly: Guid("baf8082b-8dd8-4e7c-9c5b-d9213e08c3b1")] |
|||
|
|||
// Version information for an assembly consists of the following four values:
|
|||
//
|
|||
// Major Version
|
|||
// Minor Version
|
|||
// Build Number
|
|||
// Revision
|
|||
//
|
|||
// You can specify all the values or you can default the Build and Revision Numbers
|
|||
// by using the '*' as shown below:
|
|||
// [assembly: AssemblyVersion("1.0.*")]
|
|||
[assembly: AssemblyVersion("1.0.0.0")] |
|||
[assembly: AssemblyFileVersion("1.0.0.0")] |
|||
Binary file not shown.
Binary file not shown.
Loading…
Reference in new issue