下位机使用北京大华程控电源DH1766-1,上位机使用WPF。实现了电压电流实时采集,曲线显示。上午在公司调试成功,手头没有程控电源,使用TCP服务端模拟。昨天写的TCP服务端正好排上用场。

界面如下:

服务端

服务端实在上篇基础上实现的。需要做如下更改:

  1. while (true)
  2.                           {
  3.                               try
  4.                               {
  5.                                   byte[] bufferDate = new byte[1024];
  6.                                   int realLen = pSocket.Receive(bufferDate);
  7.                                   if (realLen <= 0)
  8.                                   {
  9.                                       this.Invoke(addTextDelegate, pSocket.RemoteEndPoint.ToString() + "退出\r\n");
  10.                                       socketList.Remove(pSocket);
  11.                                       //客户端退出的时候会发送一个空字节
  12.                                       pSocket.Shutdown(SocketShutdown.Both);
  13.                                       pSocket.Close();
  14.                                       return;
  15.                                   }
  16.                                   string receiveStr = Encoding.Default.GetString(bufferDate, 0, realLen);
  17.                                   switch (receiveStr)
  18.                                   {
  19.                                       case "MEAS:VOLTage:ALL?\n":
  20.                                           proxSocket.Send(Encoding.Default.GetBytes(r.Next(16,25).ToString()+ ","+r.Next(16, 25).ToString()+","+ r.Next(16, 25).ToString()));
  21.                                           break;
  22.                                       case "MEAS:CURR:ALL?\n":
  23.                                           proxSocket.Send(Encoding.Default.GetBytes(r.Next(2, 5).ToString() + "," + r.Next(2, 5).ToString() + "," + r.Next(2, 5).ToString()));
  24.                                           break;
  25.                                       default:
  26.                                           break;
  27.                                   }
  28.                                   this.Invoke(addTextDelegate, receiveStr + "from" + pSocket.RemoteEndPoint.ToString() + "\r\n");
  29.                               }
  30.                               catch (Exception ex)
  31.                               {
  32.                                   this.Invoke(addTextDelegate, pSocket.RemoteEndPoint.ToString() + "异常退出\r\n");
  33.                                   socketList.Remove(pSocket);
  34.                                   pSocket.Shutdown(SocketShutdown.Both);
  35.                                   pSocket.Close();
  36.                                   return;
  37.                               }
  38.                           }

在While循环中加入:

switch (receiveStr)
{
  case "MEAS:VOLTage:ALL?\n":
  proxSocket.Send(Encoding.Default.GetBytes(r.Next(16,25).ToString()+ ","+r.Next(16, 25).ToString()+","+ r.Next(16, 25).ToString()));
  break;
  case "MEAS:CURR:ALL?\n":
  proxSocket.Send(Encoding.Default.GetBytes(r.Next(2, 5).ToString() + "," + r.Next(2, 5).ToString() + "," + r.Next(2, 5).ToString()));
  break;
  default:
  break;

}

模拟电源,当收到电压查询时,发送16~25中随机数,由于电源是三个通道的,因此发送三个随机数,用逗号隔开。同样收到电流查询,发送2~5之间的随机数。

完整的客户端源码:

  1. public partial class Form1 : Form
  2. {
  3. public Form1()
  4. {
  5. InitializeComponent();
  6. addTextDelegate = new AddTextDelegate(AddText);
  7. }
  8. private AddTextDelegate addTextDelegate;
  9. private List<Socket> socketList = new List<Socket>();
  10.  
  11. public delegate void AddTextDelegate(string text);
  12. private void AddText(string text)
  13. {
  14. txtLog.Text += text;
  15. }
  16.  
  17. Random r = new Random();
  18.  
  19. private void btnStart_Click(object sender, EventArgs e)
  20. {
  21. //参数:寻址方式 传输数据方式 通信协议
  22. Socket socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
  23.  
  24. IPAddress iPAddress = IPAddress.Parse(txtIP.Text);
  25.  
  26. //创建EndPoint
  27. IPEndPoint iPEndPoint = new IPEndPoint(iPAddress, int.Parse(txtPort.Text));
  28.  
  29. //绑定端口
  30. socket.Bind(iPEndPoint);
  31.  
  32. //开启侦听
  33. socket.Listen(10);
  34.  
  35. txtLog.Text += "服务启动开启侦听……\r\n";
  36.  
  37. Thread thread = new Thread((s) =>
  38. {
  39. Socket serSocket = (Socket)s;
  40. while (true)//不断接收客户端连接
  41. {
  42. this.Invoke(addTextDelegate, "服务正在等待客户端连接……\r\n");
  43.  
  44. //开始接收客户端的连接
  45. //阻塞当前线程,等待客户端连接
  46. //客户端连接上之后,服务端自动生成一个socket和连接的客端通信
  47. Socket proxSocket = serSocket.Accept();
  48.  
  49. this.Invoke(addTextDelegate, "客户端连接成功!\r\n" + proxSocket.RemoteEndPoint.ToString());
  50.  
  51. //proxSocket.Send(Encoding.Default.GetBytes("连接成功!"));
  52.  
  53. socketList.Add(proxSocket);//当前通信的socket放到集合中
  54.  
  55. new Thread(p =>
  56. {
  57. Socket pSocket = (Socket)p;
  58. while (true)
  59. {
  60. try
  61. {
  62. byte[] bufferDate = new byte[1024];
  63. int realLen = pSocket.Receive(bufferDate);
  64.  
  65. if (realLen <= 0)
  66. {
  67. this.Invoke(addTextDelegate, pSocket.RemoteEndPoint.ToString() + "退出\r\n");
  68.  
  69. socketList.Remove(pSocket);
  70. //客户端退出的时候会发送一个空字节
  71. pSocket.Shutdown(SocketShutdown.Both);
  72. pSocket.Close();
  73.  
  74. return;
  75. }
  76. string receiveStr = Encoding.Default.GetString(bufferDate, 0, realLen);
  77. switch (receiveStr)
  78. {
  79. case "MEAS:VOLTage:ALL?\n":
  80. proxSocket.Send(Encoding.Default.GetBytes(r.Next(16,25).ToString()+ ","+r.Next(16, 25).ToString()+","+ r.Next(16, 25).ToString()));
  81. break;
  82. case "MEAS:CURR:ALL?\n":
  83. proxSocket.Send(Encoding.Default.GetBytes(r.Next(2, 5).ToString() + "," + r.Next(2, 5).ToString() + "," + r.Next(2, 5).ToString()));
  84. break;
  85. default:
  86. break;
  87. }
  88. this.Invoke(addTextDelegate, receiveStr + "from" + pSocket.RemoteEndPoint.ToString() + "\r\n");
  89. }
  90. catch (Exception ex)
  91. {
  92. this.Invoke(addTextDelegate, pSocket.RemoteEndPoint.ToString() + "异常退出\r\n");
  93.  
  94. socketList.Remove(pSocket);
  95. pSocket.Shutdown(SocketShutdown.Both);
  96. pSocket.Close();
  97. return;
  98. }
  99. }
  100. })
  101. { IsBackground = true }.Start(proxSocket);
  102. }
  103. });
  104. thread.IsBackground = true;
  105. thread.Start(socket);
  106.  
  107. }
  108.  
  109. private void btnSend_Click(object sender, EventArgs e)
  110. {
  111. string str = txtSend.Text;
  112. byte[] data = Encoding.Default.GetBytes(str);
  113. foreach (var socket in socketList)
  114. {
  115. if (socket != null && socket.Connected)
  116. {
  117. socket.Send(data);
  118. }
  119. }
  120. }
  121. }

上位机实现客户端功能。具体如下:

1、字段和属性

public readonly IPEndPoint TagetIPEP;
public bool IsConnected { get; set; } = false;
private Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) { /*ReceiveTimeout=1000,SendTimeout=1000*/};
private Thread recListenThread;
public string ReceiveStr { get; set; }
public byte[] ReceiveByte { get; set; }
TagetIPEP是服务器地址和端口。
IsConnected是连接的状态,这个比较重要,在发送和接收时,都要更加IsConnected进行,并更新IsConnected。
Socket用于和客户端通讯。
recListenThread是监听客户端消息的线程。
ReceiveStr和ReceiveByte用来存储客户端发来的消息。
2、方法函数
连接方法:
  1. public bool Connect()
  2. {
  3. try
  4. {
  5. socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
  6. {
  7. //ReceiveTimeout = 1000,
  8. //SendTimeout=1000
  9. };
  10.  
  11. //IAsyncResult connResult = socket.BeginConnect(TagetIPEP.Address, TagetIPEP.Port, null, null);
  12. //connResult.AsyncWaitHandle.WaitOne(5000, true);
  13. //if (connResult.IsCompleted)
  14. //{
  15. socket.Connect(TagetIPEP.Address, TagetIPEP.Port);
  16. IsConnected = true;
  17. //开启接收监听
  18.  
  19. recListenThread = new Thread(() =>
  20. {
  21. while (true)
  22. {
  23. try
  24. {
  25. ReceiveByte = new byte[1024];
  26. int realLen = socket.Receive(ReceiveByte);
  27. ReceiveStr = Encoding.Default.GetString(ReceiveByte, 0, realLen);
  28. ReceiveEvent();
  29. if (realLen <= 0)
  30. {
  31. if (socket != null && socket.Connected)
  32. {
  33. //服务器退出
  34. IsConnected = false;
  35. Log.WriteLog("服务器退出!");
  36. socket.Shutdown(SocketShutdown.Both);
  37. socket.Close();
  38. MessageBox.Show("连接断开!");
  39. }
  40. return;
  41. }
  42. }
  43. catch (Exception ex)
  44. {
  45. if (socket != null && socket.Connected)
  46. {
  47. IsConnected = false;
  48. Log.WriteLog("服务器异常退出!", ex);
  49. socket.Shutdown(SocketShutdown.Both);
  50. socket.Close();
  51. }
  52. return;
  53. }
  54. }
  55. })
  56. { IsBackground = true };
  57. recListenThread.Start();
  58. return true;
  59. //}
  60.  
  61. }
  62. catch (Exception ex)
  63. {
  64. Log.WriteLog(TagetIPEP.Address + "连接失败", ex);
  65. }
  66. return false;
  67. }

连接函数返回值为bool类型,根据返回值判断连接是否成功连接。这里每次连接都实例化了一个socket,因为在执行socket.close()后,重新打开会失败,而断线重连会经常用到,没有找到更好的方法,干脆重新实例化socket。连接成功后,开启监听服务端消息的线程。这里使用了一个ReceiveEvent()事件,在接收到消息时会触发这个事件,刷新UI界面。

发送方法:

  1. public bool Send(string msg)
  2. {
  3. byte[] sendMsg = Encoding.Default.GetBytes(msg);
  4. if (sendMsg.Length > 0&&IsConnected)
  5. {
  6. if (socket != null && socket.Connected)
  7. {
  8. try
  9. {
  10. socket.Send(sendMsg);
  11. return true;
  12. }
  13. catch (Exception ex)
  14. {
  15. IsConnected = false;
  16. Log.WriteLog("发送数据失败,目标地址" + TagetIPEP.Address, ex);
  17. }
  18. }
  19. }
  20.  
  21. return false;
  22.  
  23. }

关闭方法:

  1. public void Close()
  2. {
  3. if (socket != null && socket.Connected)
  4. {
  5. IsConnected = false;
  6. recListenThread.Abort();
  7. Log.WriteLog("关闭连接!");
  8. socket.Shutdown(SocketShutdown.Both);
  9. socket.Close();
  10. }
  11. }

在出现异常时调用

消息接收事件:

public event Action ReceiveEvent;

每次接收消息时触发,获取属性ReceiveStr和ReceiveByte的值,刷新UI界面。

完整代码:

  1. public class TCPClient
  2. {
  3. public TCPClient(/*IPEndPoint localIPEP,*/IPEndPoint targetIPEP)
  4. {
  5. //socket.Bind(localIPEP);
  6. TagetIPEP = targetIPEP;
  7.  
  8. }
  9.  
  10. public readonly IPEndPoint TagetIPEP;
  11.  
  12. public bool IsConnected { get; set; } = false;
  13.  
  14. private Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) { /*ReceiveTimeout=1000,SendTimeout=1000*/};
  15.  
  16. public bool Connect()
  17. {
  18. try
  19. {
  20. socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
  21. {
  22. //ReceiveTimeout = 1000,
  23. //SendTimeout=1000
  24. };
  25.  
  26. //IAsyncResult connResult = socket.BeginConnect(TagetIPEP.Address, TagetIPEP.Port, null, null);
  27. //connResult.AsyncWaitHandle.WaitOne(5000, true);
  28. //if (connResult.IsCompleted)
  29. //{
  30. socket.Connect(TagetIPEP.Address, TagetIPEP.Port);
  31. IsConnected = true;
  32. //开启接收监听
  33.  
  34. recListenThread = new Thread(() =>
  35. {
  36. while (true)
  37. {
  38. try
  39. {
  40. ReceiveByte = new byte[1024];
  41. int realLen = socket.Receive(ReceiveByte);
  42. ReceiveStr = Encoding.Default.GetString(ReceiveByte, 0, realLen);
  43. ReceiveEvent();
  44. if (realLen <= 0)
  45. {
  46. if (socket != null && socket.Connected)
  47. {
  48. //服务器退出
  49. IsConnected = false;
  50. Log.WriteLog("服务器退出!");
  51. socket.Shutdown(SocketShutdown.Both);
  52. socket.Close();
  53. MessageBox.Show("连接断开!");
  54. }
  55. return;
  56. }
  57. }
  58. catch (Exception ex)
  59. {
  60. if (socket != null && socket.Connected)
  61. {
  62. IsConnected = false;
  63. Log.WriteLog("服务器异常退出!", ex);
  64. socket.Shutdown(SocketShutdown.Both);
  65. socket.Close();
  66. }
  67. return;
  68. }
  69. }
  70. })
  71. { IsBackground = true };
  72. recListenThread.Start();
  73. return true;
  74. //}
  75.  
  76. }
  77. catch (Exception ex)
  78. {
  79. Log.WriteLog(TagetIPEP.Address + "连接失败", ex);
  80. }
  81. return false;
  82. }
  83.  
  84. public bool Send(string msg)
  85. {
  86. byte[] sendMsg = Encoding.Default.GetBytes(msg);
  87. if (sendMsg.Length > 0&&IsConnected)
  88. {
  89. if (socket != null && socket.Connected)
  90. {
  91. try
  92. {
  93. socket.Send(sendMsg);
  94. return true;
  95. }
  96. catch (Exception ex)
  97. {
  98. IsConnected = false;
  99. Log.WriteLog("发送数据失败,目标地址" + TagetIPEP.Address, ex);
  100. }
  101. }
  102. }
  103.  
  104. return false;
  105.  
  106. }
  107.  
  108. public event Action ReceiveEvent;
  109.  
  110. public string ReceiveStr { get; set; }
  111.  
  112. public byte[] ReceiveByte { get; set; }
  113.  
  114. public void Close()
  115. {
  116. if (socket != null && socket.Connected)
  117. {
  118. IsConnected = false;
  119. recListenThread.Abort();
  120. Log.WriteLog("关闭连接!");
  121. socket.Shutdown(SocketShutdown.Both);
  122. socket.Close();
  123. }
  124. }
  125.  
  126. private Thread recListenThread;
  127.  
  128. }

前台调用,声明Timer定时器,每个一秒触发一次。触发事件如下:

  1. private string flag = "";
  2. private void QueryTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
  3. {
  4. Now = DateTime.Now;
  5.  
  6. if (!tcp.Send("MEAS:VOLTage:ALL?\n"))
  7. {
  8. queryTimer.Enabled = false;
  9. StartContent = "开始";
  10. ConnContent = "连接";
  11. tcp.IsConnected = false;
  12. MessageBox.Show("查询失败!");
  13. return;
  14. }
  15. flag = "V";
  16. Thread.Sleep();
  17.  
  18. if (!tcp.Send("MEAS:CURR:ALL?\n"))
  19. {
  20. queryTimer.Enabled = false;
  21. StartContent = "开始";
  22. ConnContent = "连接";
  23. tcp.IsConnected = false;
  24. MessageBox.Show("查询失败!");
  25. return;
  26. }
  27. flag = "C";
  28.  
  29. #region 测试
  30. //angle += 18;
  31. //if (angle > 360)
  32. //{
  33. // angle = 18;
  34. //}
  35.  
  36. #endregion
  37.  
  38. }

刷新UI界面的事件如下:

  1. private void Tcp_ReceiveEvent()
  2. {
  3. Task.Run(() =>
  4. {
  5. Application.Current.Dispatcher.Invoke(() =>
  6. {
  7. RemoteIP = tcp.TagetIPEP.ToString();
  8. switch (flag)
  9. {
  10. case "V":
  11. VoltValue = Math.Round(Convert.ToDouble(tcp.ReceiveStr.Split(]), );
  12. break;
  13. case "C":
  14. CurrentValue = Math.Round(Convert.ToDouble(tcp.ReceiveStr.Split(]), );
  15. break;
  16. default:
  17. break;
  18. }
  19.  
  20. #region 测试
  21. //VoltValue = Math.Round(Math.Sin((angle) * pi / 180) * 16 + 16, 3);
  22. //CurrentValue = Math.Round(Math.Sin((angle) * pi / 180) * 2.5 + 2.5, 3);
  23. #endregion
  24.  
  25. VoltValues.Add(VoltValue);
  26. CurrentValues.Add(CurrentValue);
  27.  
  28. )
  29. {
  30. VoltValues.RemoveAt();
  31. CurrentValues.RemoveAt();
  32. }
  33. });
  34. });
  35. }

C# WPF上位机实现和下位机TCP通讯的更多相关文章

  1. C#做上位机软件——绘图并传输给下位机

    拿到任务之后首先分成了几个部分: 1.绘图.学习了GDI+ 2.图片保存. 3.将图片转换成byte[].由于使用Socket通信,只能传输byte[]数据,所以这一步是向下位机传输的关键. 相应地, ...

  2. WPF开发汽车采样机上位机软件

    由于项目需要,需开发同一套汽车.火车.皮带采样机的上位机软件. 看过之前的上位机软件,老版本都是DelPhi.VB开发,稍微新语言开发的是采用winform开发.要不就是使用组态软件. Delphi语 ...

  3. 嵌入式Linux学习笔记(六) 上位机QT界面实现和串口通讯实现

    目录 (1).参考资料 (2).QT界面布局实现 (3).数据和操作逻辑 在上一章我们实现了下位机的协议制定,并通过串口通讯工具完成了对设备内外设(LED)的状态修改,下面就要进行上位机软件的实现了( ...

  4. vb配置下位机CAN寄存器小结

    2011-12-14 18:44:32 效果图 1,完成设计(由于没有eeprom等存储设备,所以每次上电后需要通过串口配置某些寄存器).在设计中,列出技术评估难度,并进行尝试,参看<我的设计& ...

  5. "废物利用"也抄袭——“完全”DIY"绘图仪"<二、下位机程序设计>

    就不说怎么组装了吧,一把辛酸泪.说程序,因为这有两把辛酸泪……一把给下位机的C代码一把为了VB.NET的图像处理……不过就上上一篇说的,它们可以正确运行了,并且今天克服了Arduino上电过程中步进电 ...

  6. STM32 使用Cubemx 建一个USB(HID)设备下位机,实现数据收发

    这里我主要说一下如何做一个USB下位机,这里主要分3部分:1.建立工程:2.添加报文描述符:3.数据的传输.这里就不讲USB的理论知识了,有想要了解的自行百度一下就可以了. 建立工程:工程建立参考:h ...

  7. 转载 STM32 使用Cubemx 建一个USB(HID)设备下位机,实现数据收发

    STM32 使用Cubemx 建一个USB(HID)设备下位机,实现数据收发  本文转载自 https://www.cnblogs.com/xingboy/p/9913963.html 这里我主要说一 ...

  8. bootargs说明 和 下位机静态ip的设置

    setenv bootargs root=/dev/nfs       nfsroot=192.168.1.8:/opt/rootfs      ip=192.168.1.110:192.168.1. ...

  9. C#使用struct直接转换下位机数据

    编写上位机与下位机通信的时候,涉及到协议的转换,比较多会使用到二进制.传统的方法,是将数据整体获取到byte数组中,然后逐字节对数据进行解析.这样操作工作量比较大,对于较长数据段更容易计算位置出错. ...

随机推荐

  1. java导出excel,多表头合并

    要求结果图如下: 有空补充具体逻辑 参考:https://blog.csdn.net/dj0721/article/details/72463042 HSSFColor  背景颜色选择  参考:htt ...

  2. win10配置java开发环境

    安装java(JDK) Oracle官网下载地址 选择JavaSE > Downloads 选择最新版本 > Download Accept License Agreement 选择要下载 ...

  3. Mysql 5.7--ubuntu18.04 安装过程及遇到的问题

    Mysql 5.7安装过程 1. 下载mysql的apt-config文件 a. https://dev.mysql.com/downloads/file/?id=477124 b. 点击downlo ...

  4. C# DataGridView 动态添加列和调整列顺序

    https://yq.aliyun.com/articles/421700 // DataGridView1的ColumnDisplayIndexChanged事件处理方法private void D ...

  5. 论文笔记:Fast Online Object Tracking and Segmentation: A Unifying Approach

    Fast Online Object Tracking and Segmentation: A Unifying Approach CVPR-2019 2019-03-11 23:45:12 Pape ...

  6. JavaScript 数组去重方法总结

    1.遍历数组法: 这应该是最简单的去重方法(实现思路:新建一新数组,遍历数组,值不在新数组就加入该新数组中) // 遍历数组去重法 function unique(arr){ var _arr = [ ...

  7. Vue-admin工作整理(十六):Ajax-axios进行请求封装+拦截器

    典型的工具类封装,增加拦截起来做相应的处理 user.js: import axios from './index' export const getUserInfo = ({ userId }) = ...

  8. Cow Cycling 动态规划

    1552: Cow Cycling 时间限制(普通/Java):1000MS/10000MS     内存限制:65536KByte总提交: 39            测试通过:20 描述 The ...

  9. selenium-xpath练习

  10. 如何只安装Postgresql client(以9.4 为例)

    Install the repository RPM: yum install https://download.postgresql.org/pub/repos/yum/9.4/redhat/rhe ...