Commit 6b1a51e0 LN

增加AIOBOX类

1 个父辈 b9c450c0
...@@ -36,9 +36,6 @@ ...@@ -36,9 +36,6 @@
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Asa.IOModule.AIOBOX">
<HintPath>..\..\dll\Asa.IOModule.AIOBOX.dll</HintPath>
</Reference>
<Reference Include="Client, Version=1.0.0.1, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="Client, Version=1.0.0.1, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\..\dll\Client.dll</HintPath> <HintPath>..\..\dll\Client.dll</HintPath>
...@@ -84,6 +81,7 @@ ...@@ -84,6 +81,7 @@
<Compile Include="baan\AxisBean.cs" /> <Compile Include="baan\AxisBean.cs" />
<Compile Include="baan\WaitUtil.cs" /> <Compile Include="baan\WaitUtil.cs" />
<Compile Include="deviceLibrary\halcon\CodeManager.cs" /> <Compile Include="deviceLibrary\halcon\CodeManager.cs" />
<Compile Include="deviceLibrary\IO\AIOBOX\AIOBOX.cs" />
<Compile Include="deviceLibrary\IO\AIOBOX\AIOBOXManager.cs" /> <Compile Include="deviceLibrary\IO\AIOBOX\AIOBOXManager.cs" />
<Compile Include="deviceLibrary\IO\IOManager.cs" /> <Compile Include="deviceLibrary\IO\IOManager.cs" />
<Compile Include="assemblyLine\LineBean.cs" /> <Compile Include="assemblyLine\LineBean.cs" />
......
using OnlineStore.Common;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace Asa.IOModule
{
/// <summary>
/// 零点IO模块操作类
/// </summary>
public class AIOBOX
{
private string LogName {
get { return "AIOBOX [" + IP + "]"; }
}
/// <summary>
/// 自动读取DI委托
/// </summary>
/// <param name="box">AIOBOX</param>
/// <param name="sta">所有DI状态</param>
public delegate void DIO_Changed(AIOBOX box, Box_Sta[] sta);
/// <summary>
/// 自动读取DI事件触发
/// </summary>
public event DIO_Changed DI_Changed_Event;
/// <summary>
/// 自动读取DO事件触发
/// </summary>
public event DIO_Changed DO_Changed_Event;
private Socket _client; //客户端
private bool _loop;
private ushort _uid;
private Box_Sta[] _stateDI; //DI状态
private Box_Sta[] _stateDO; //DO状态
private int[] _valueAI; //AI模拟量
private int[] _valueAO; //AO模拟量
private List<byte> _receive;
private System.Collections.Concurrent.ConcurrentQueue<byte[]> _writeDO;
private log4net.ILog log;
private readonly Box_Type _typeInput; //输入类型
private readonly Box_Type _typeOutput; //输出类型
private readonly byte[] _addressInput; //输入地址
private readonly byte[] _addressOutput; //输出地址
private const int SEND_SLEEP = 50; //每条命令发送的间隔,不能小于15,会出现IO接收不到的情况,小于30时,会出现接收数据连包的情况
private const int PORT = 502; //端口
private const int UPLOAD_TIME = 8000; //8秒
private Thread tRecon; //重连线程
private Thread tSend; //发送命令处理
private Thread tListen; //监听网络
private UdpClient broadcastClient; //广播客户端
private IPEndPoint broadcastEndPoint; //广播远程节点
private byte[] broadcastBuffer; //远程返回数据
/// <summary>
/// 零点IO模块操作类
/// </summary>
/// <param name="input">输入类型</param>
/// <param name="inputCount">输入数量</param>
/// <param name="output">输出类型</param>
/// <param name="outputCount">输出数量</param>
public AIOBOX(Box_Type input, int inputCount, Box_Type output, int outputCount)
{
_typeInput = input;
_typeOutput = output;
log = log4net.LogManager.GetLogger("AIOBOX");
byte n = 0;
_addressInput = new byte[inputCount];
for (int i = 0; i < inputCount; i++)
_addressInput[i] = n++;
_addressOutput = new byte[outputCount];
for (int i = 0; i < outputCount; i++)
_addressOutput[i] = n++;
_stateDI = new Box_Sta[inputCount];
_stateDO = new Box_Sta[outputCount];
_valueAI = new int[inputCount];
_valueAO = new int[outputCount];
}
/// <summary>
/// IP地址
/// </summary>
public string IP { set; get; } = "192.168.1.100";
/// <summary>
/// 输入主动上传
/// </summary>
public bool Upload { set; get; } = false;
/// <summary>
/// 是否连接
/// </summary>
public bool IsConn { get; private set; } = false;
/// <summary>
/// 错误信息
/// </summary>
public string ErrInfo { get; private set; } = "";
/// <summary>
/// 监控DI输入,索引数组
/// </summary>
public int[] MonitorDI { get; set; }
/// <summary>
/// 监控DO输出,索引数组
/// </summary>
public int[] MonitorDO { get; set; }
/// <summary>
/// 连接
/// </summary>
public void Connect()
{
_loop = true;
_uid = 0;
IsConn = false;
_receive = new List<byte>();
_writeDO = new System.Collections.Concurrent.ConcurrentQueue<byte[]>();
tRecon = new Thread(new ThreadStart(Reconn));
tSend = new Thread(new ThreadStart(Send));
tListen = new Thread(new ThreadStart(Listen));
tRecon.Start();
tSend.Start();
tListen.Start();
log.Info("Connect");
}
/// <summary>
/// 关闭连接
/// </summary>
public void Close()
{
_loop = false;
IsConn = false;
try
{
if (_client != null)
{
_client.Shutdown(SocketShutdown.Both);
_client.Close();
}
log.Info("Close");
}
catch (Exception ex)
{
log.Error("Close", ex);
}
finally
{
_client = null;
}
}
/// <summary>
/// 相反状态(ON/OFF)
/// </summary>
/// <param name="sta"></param>
/// <returns></returns>
public Box_Sta ReverseStatus(Box_Sta sta)
{
return sta == Box_Sta.On ? Box_Sta.Off : Box_Sta.On;
}
/// <summary>
/// 相反状态(ON/OFF)
/// </summary>
/// <param name="sta"></param>
public void ReverseStatus(ref Box_Sta sta)
{
if (sta == Box_Sta.On)
sta = Box_Sta.Off;
else
sta = Box_Sta.On;
}
/// <summary>
/// 读取单个DI输入状态(ON/OFF)
/// </summary>
/// <param name="add">输入地址,从0开始</param>
/// <returns></returns>
public Box_Sta ReadDI(int add)
{
return _stateDI[add];
}
/// <summary>
/// 读取多个DI输入状态(ON/OFF)
/// </summary>
/// <param name="add">起始地址,从0开始</param>
/// <param name="count">数量</param>
/// <returns></returns>
public Box_Sta[] ReadDI(int add, int count)
{
Box_Sta[] sta = new Box_Sta[count];
Array.Copy(_stateDI, add, sta, 0, count);
return sta;
}
/// <summary>
/// 读取单个AI模拟量的值
/// </summary>
/// <param name="add">模拟量地址,从0开始</param>
/// <returns></returns>
public int ReadAI(int add)
{
return _valueAI[add];
}
/// <summary>
/// 读取多个AI模拟量的值
/// </summary>
/// <param name="add">起始地址,从0开始</param>
/// <param name="count">数量</param>
/// <returns></returns>
public int[] ReadAI(int add, int count)
{
int[] sta = new int[count];
Array.Copy(_valueAI, add, sta, 0, count);
return sta;
}
/// <summary>
/// 读取单个DO输出状态(ON/OFF)
/// </summary>
/// <param name="add">输出地址,从0开始</param>
/// <returns></returns>
public Box_Sta ReadDO(int add)
{
return _stateDO[add];
}
/// <summary>
/// 读取多个DO输出状态(ON/OFF)
/// </summary>
/// <param name="add">起始地址,从0开始</param>
/// <param name="count">数量</param>
/// <returns></returns>
public Box_Sta[] ReadDO(int add, int count)
{
Box_Sta[] sta = new Box_Sta[count];
Array.Copy(_stateDO, add, sta, 0, count);
return sta;
}
/// <summary>
/// 读取单个AO模拟量的值
/// </summary>
/// <param name="add">输出地址,从0开始</param>
/// <returns></returns>
public int ReadAO(int add)
{
return _valueAO[add];
}
/// <summary>
/// 读取多个AO模拟量的值
/// </summary>
/// <param name="add">起始地址,从0开始</param>
/// <param name="count">数量</param>
/// <returns></returns>
public int[] ReadAO(int add, int count)
{
int[] sta = new int[count];
Array.Copy(_valueAO, add, sta, 0, count);
return sta;
}
/// <summary>
/// 写入单个DO输出状态(ON/OFF)
/// </summary>
/// <param name="add">输出地址,从0开始</param>
/// <param name="sta"></param>
/// <returns></returns>
public bool WriteDO(int add, Box_Sta sta)
{
try
{
log.Info("call WriteDO");
byte[] data = Command();
byte[] buff = new byte[12];
Array.Copy(data, 0, buff, 0, data.Length);
buff[5] = 6; //后面字节数
buff[7] = 5; //功能码
buff[9] = _addressOutput[add]; //地址
buff[10] = (byte)sta; //写入值
_writeDO.Enqueue(buff);
return true;
}
catch (Exception ex)
{
LogUtil.error(LogName + "WriteDO ", ex);
log.Error("WriteDO ", ex);
return false;
}
}
/// <summary>
/// 发送命令线程
/// </summary>
private void Send()
{
bool shift = true; //切换
int time = 0; //上传时间
int writeTimer = 0; //写入DO次数
bool writeStop = false; //写入命令停止
while (_loop)
{
Thread.Sleep(SEND_SLEEP);
if (!IsConn) continue;
byte[] buff;
if (Upload) //主动上传
{
if (time >= UPLOAD_TIME)
{
buff = GetReadDI_Command();
time = 0;
}
else
{
if (writeTimer >= 3) //连续发送WriteDO3次,强制发送DO
{
buff = GetReadDO_Command();
writeTimer = 0;
}
else
{
if (_writeDO.TryDequeue(out buff))
{
writeTimer++;
}
else
{
buff = GetReadDO_Command();
writeTimer = 0;
}
}
}
time += SEND_SLEEP;
}
else
{
if (writeTimer >= 3) //连续发送WriteDO3次,强制发送DI和DO
{
if (shift)
buff = GetReadDI_Command();
else
buff = GetReadDO_Command();
shift = !shift;
if (writeStop)
writeTimer = 0;
else
writeStop = true;
}
else
{
if (_writeDO.TryDequeue(out buff))
{
writeTimer++;
writeStop = false;
}
else
{
if (shift)
buff = GetReadDI_Command();
else
buff = GetReadDO_Command();
shift = !shift;
writeTimer = 0;
}
}
}
try
{
_client.Send(buff);
log.Debug("Send: " + HexBuff(buff));
}
catch (Exception ex)
{
LogUtil.error(LogName + "Send ", ex);
log.Error("Send ", ex);
log.Info("Socket Close");
IsConn = false;
}
}
}
/// <summary>
/// 获取ReadDI的命令
/// </summary>
/// <returns></returns>
private byte[] GetReadDI_Command()
{
byte[] data = Command();
byte[] buff = new byte[12];
Array.Copy(data, 0, buff, 0, data.Length);
buff[5] = 6; //后面字节数
//功能码
if (_typeInput == Box_Type.DI)
buff[7] = 2;
else if (_typeInput == Box_Type.AI)
buff[7] = 4;
buff[9] = _addressInput[0]; //地址
buff[11] = (byte)_addressInput.Length; //个数
return buff;
}
/// <summary>
/// 获取ReadDO的命令
/// </summary>
/// <returns></returns>
private byte[] GetReadDO_Command()
{
byte[] data = Command();
byte[] buff = new byte[12];
Array.Copy(data, 0, buff, 0, data.Length);
buff[5] = 6; //后面字节数
buff[7] = 1; //功能码
buff[9] = _addressOutput[0]; //地址
buff[11] = (byte)_addressOutput.Length; //个数
return buff;
}
/// <summary>
/// 监听网络线程
/// </summary>
private void Listen()
{
while (_loop)
{
Thread.Sleep(10);
if (!IsConn) continue;
if (_client == null) continue;
try
{
if (_client.Available > 0)
{
byte[] buff = new byte[_client.ReceiveBufferSize];
int count = _client.Receive(buff);
byte[] temp = new byte[count];
Array.Copy(buff, 0, temp, 0, count);
lock (_receive)
{
_receive.AddRange(temp);
while (_receive.Count > 6) //分解连包
{
int len = _receive[5] + 6;
if (len > _receive.Count) break;
byte[] cmd = new byte[len];
_receive.CopyTo(0, cmd, 0, len);
_receive.RemoveRange(0, len);
log.Debug("Receive: " + HexBuff(cmd));
if (IP.Equals("192.168.101.22") && cmd.Length>7&& (cmd[7] == 2))
{
LogUtil.LOGGER.Info(LogName + " Listen 收到数据 " + HexBuff(cmd));
}
System.Threading.Tasks.Task.Run(() => CommandProcess(cmd));
}
}
}
}
catch (Exception ex)
{
LogUtil.error(LogName + "Listen ",ex);
log.Error("Listen ", ex);
IsConn = false;
}
}
}
/// <summary>
/// 接收到的命令处理方法,(task多线程)
/// </summary>
/// <param name="cmd"></param>
private void CommandProcess(byte[] cmd)
{
string str;
int idx = 0;
int count = cmd[8];
try
{
if (cmd[7] == 1) //ReadDO
{
Box_Sta[] staDO = new Box_Sta[_stateDO.Length];
lock (_stateDO)
{
for (int i = 1; i <= count; i++)
{
int move = 0;
byte val = cmd[8 + i];
for (int j = 0; j < 8; j++) //字节的0-7位
{
int n = (val & Convert.ToInt32(Math.Pow(2, move))) >> move;
_stateDO[idx++] = n == 1 ? Box_Sta.On : Box_Sta.Off;
move++;
}
}
Array.Copy(_stateDO, 0, staDO, 0, staDO.Length);
if (MonitorDO != null)
{
str = "MonitorDO ";
for (int i = 0; i < MonitorDO.Length; i++)
str += string.Format("[{0}]{1} ", MonitorDO[i], _stateDO[MonitorDO[i]].ToString());
log.Info(str);
}
}
DO_Changed_Event?.Invoke(this, staDO);
}
else if (cmd[7] == 2) //ReadDI
{
Box_Sta[] staDI = new Box_Sta[_stateDI.Length];
lock (_stateDI)
{
for (int i = 1; i <= count; i++)
{
int move = 0;
byte val = cmd[8 + i];
for (int j = 0; j < 8; j++) //字节的0-7位
{
int n = (val & Convert.ToInt32(Math.Pow(2, move))) >> move;
_stateDI[idx++] = n == 1 ? Box_Sta.On : Box_Sta.Off;
move++;
}
}
Array.Copy(_stateDI, 0, staDI, 0, staDI.Length);
if (MonitorDI != null)
{
str = "MonitorDI ";
for (int i = 0; i < MonitorDI.Length; i++)
str += string.Format("[{0}]{1} ", MonitorDI[i], _stateDI[MonitorDI[i]].ToString());
log.Info(str);
}
}
if (IP.Equals("192.168.101.22"))
{
string msg = "";
foreach (Box_Sta s in staDI)
{
msg += " " + (int)s;
}
LogUtil.LOGGER.Info(LogName + " DI_Changed_Event " + msg);
}
DI_Changed_Event?.Invoke(this, staDI);
}
else if (cmd[7] == 5)
{
}
}
catch (Exception ex)
{
LogUtil.error(LogName + "CommandProcess ", ex);
ErrInfo = ex.Message;
}
}
/// <summary>
/// 重连线程
/// </summary>
private void Reconn()
{
while (_loop)
{
if (IsConn)
{
Thread.Sleep(1000);
}
else
{
Thread.Sleep(100);
if (_loop) Open();
}
}
}
/// <summary>
/// 打开socket建立连接
/// </summary>
private void Open()
{
try
{
IsConn = false;
bool rtn = CheckIP(IP);
if (!rtn) return;
//建立连接
_client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 500);
_client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 500);
_client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, 1);
_client.Connect(IPAddress.Parse(IP), PORT);
Thread.Sleep(100); //需要等待一会才能获取连接状态
IsConn = true;
log.Info("Socket Connect");
}
catch (Exception ex)
{
IsConn = false;
log.Error("Open ", ex);
LogUtil.error(LogName + "Open ", ex);
}
}
/// <summary>
/// 检查IP地址
/// </summary>
/// <param name="ip"></param>
/// <returns></returns>
private bool CheckIP(string ip)
{
//IP合法
string pattern = @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$";
bool rtn = System.Text.RegularExpressions.Regex.IsMatch(ip, pattern);
if (!rtn)
{
ErrInfo = "非法的IP地址";
return false;
}
//Ping服务端
System.Net.NetworkInformation.Ping ping = new System.Net.NetworkInformation.Ping();
System.Net.NetworkInformation.PingReply result = ping.Send(ip, 1000);
ping.Dispose();
if (result.Status != System.Net.NetworkInformation.IPStatus.Success)
{
ErrInfo = "Ping " + ip + " 请求没有响应";
return false;
}
return true;
}
/// <summary>
/// 自动获取IP地址,未连接前使用,必须在同一网段
/// </summary>
/// <param name="localIP">本地IP地址</param>
/// <returns></returns>
private bool AutoIP(string localIP)
{
try
{
IPEndPoint local = new IPEndPoint(IPAddress.Parse(localIP), 55654);
broadcastClient = new UdpClient(local);
broadcastBuffer = null;
Thread tTemp = new Thread(new ThreadStart(GetIP));
tTemp.Start();
broadcastEndPoint = new IPEndPoint(IPAddress.Parse("255.255.255.255"), 1024);
byte[] dgram = new byte[] { 0x05, 0x00, 0x07, 0x00, 0x00, 0xC1, 0x59 };
broadcastClient.Send(dgram, dgram.Length, broadcastEndPoint);
Thread.Sleep(1000);
tTemp.Abort();
broadcastClient.Close();
ErrInfo = "无法访问";
if (broadcastBuffer == null) return false;
if (broadcastBuffer[0] != 0x3B) return false;
byte[] buff = new byte[broadcastBuffer[7]];
Array.Copy(broadcastBuffer, 9, buff, 0, buff.Length);
IP = buff[buff.Length - 12] + "." + buff[buff.Length - 11] + "." + buff[buff.Length - 10] + "." + buff[buff.Length - 9];
ErrInfo = "OK";
return true;
}
catch (Exception ex)
{
ErrInfo = ex.Message;
LogUtil.error(LogName + "AutoIP ", ex);
return false;
}
}
/// <summary>
/// 获取IO模块IP地址
/// </summary>
private void GetIP()
{
broadcastBuffer = broadcastClient.Receive(ref broadcastEndPoint);
}
/// <summary>
/// 命令,前7个字节
/// </summary>
/// <returns></returns>
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.Synchronized)]
private byte[] Command()
{
byte[] flag = BitConverter.GetBytes(++_uid);
if (_uid == ushort.MaxValue) _uid = 0;
byte[] data = new byte[7];
data[0] = flag[1];
data[1] = flag[0];
data[2] = 0;
data[3] = 0;
data[4] = 0;
data[5] = 0;
data[6] = 255;
return data;
}
private string HexBuff(byte[] buff)
{
string s = "";
if (buff == null) return s;
for (int i = 0; i < buff.Length; i++)
s += buff[i].ToString("X2") + " ";
return s;
}
}
/// <summary>
/// IO模块类型
/// </summary>
public enum Box_Type : int
{
/// <summary>
/// 数字信号输入
/// </summary>
DI,
/// <summary>
/// 数字信号输出
/// </summary>
DO,
/// <summary>
/// 模拟量输入
/// </summary>
AI,
/// <summary>
/// 模拟量输出
/// </summary>
AO
}
/// <summary>
/// IO模块寄存器状态
/// </summary>
public enum Box_Sta : int
{
/// <summary>
/// 断开,关闭,低电平
/// </summary>
Off = 0,
/// <summary>
/// 闭合,打开,高电平
/// </summary>
On = 255
}
}
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!