C#——通過webapi讀寫Modbus RTU設備


22.3.14:

  跟前端實際測試后發現寫寄存器的api不能短時間內多次重復調用,采用同步請求的方式仍未解決問題,用串口監視助手監視串口后,發現只有請求消息,沒有回應消息。遂將程序中讀寫函數中的

master = ModbusSerialMaster.CreateRtu(port);

去掉,並將其添加到開閉端口的函數中,問題解決。

  猜測原因:前端采用同步請求,在第一次請求得到回應后再進行下一次請求。但原程序中api每次被調用都會產生一個IModbusMaster對象,且此時MODBUS設備未返回完成信號,即后端只是向從站發出寫入指令后即返回 回應給前端,前端據此進行下一次請求,后端接收到請求后又新建了一個IModbusMaster對象與MODBUS設備進行交互。對於MODBUS設備來說,就是一個主站給我發指令,我還沒回應,就有另一個主站給我發了另一條指令,於是就亂套了。

  

 

原文:

調用webapi控制本地程序讀寫Modbus RTU設備

例如服務開啟后,訪問http://192.168.3.252:2000/api/home/ReadMultiCoil?slaveAdd=1&address=99&number=10,可以調用webapi實現讀從站地址為1的設備中線圈起始地址為99的共十個線圈的值,返回值是JSON

其中192.168.3.252:2000為服務的HttpServer,ReadMultiCoil為定義的函數

運行的時候需要管理員權限,最好關閉防火牆或者開放端口

應該有更好的方法實現相同功能的,但我太菜了懶得找

Modbus RTU的代碼是之前的代碼改的,更改了串口關閉后沒法重新打開的問題,順便改了一下抄的代碼里Http服務關不掉的問題

 

 1 <Window x:Class="******.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 5         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 6         xmlns:local="clr-namespace:******"
 7         mc:Ignorable="d"
 8         Title="MainWindow" Height="450" Width="800">
 9     <Grid Loaded="Grid_Loaded">
10         <ComboBox x:Name="cmb_portname" HorizontalAlignment="Left" Margin="92,119,0,0" VerticalAlignment="Top" Width="120" MouseEnter="cmb_portname_MouseEnter"/>
11         <ComboBox x:Name="cmb_baud" HorizontalAlignment="Left" Margin="270,119,0,0" VerticalAlignment="Top" Width="120"/>
12         <ComboBox x:Name="cmb_parity" HorizontalAlignment="Left" Margin="440,119,0,0" VerticalAlignment="Top" Width="120"/>
13         <ComboBox x:Name="cmb_dataBits" HorizontalAlignment="Left" Margin="92,176,0,0" VerticalAlignment="Top" Width="120"/>
14         <ComboBox x:Name="cmb_stopBits" HorizontalAlignment="Left" Margin="270,176,0,0" VerticalAlignment="Top" Width="120"/>
15         <Label Content="串口" HorizontalAlignment="Left" Margin="47,119,0,0" VerticalAlignment="Top"/>
16         <Label Content="波特率" HorizontalAlignment="Left" Margin="224,119,0,0" VerticalAlignment="Top"/>
17         <Label Content="校驗" HorizontalAlignment="Left" Margin="401,119,0,0" VerticalAlignment="Top"/>
18         <Label Content="數據位" HorizontalAlignment="Left" Margin="41,176,0,0" VerticalAlignment="Top"/>
19         <Label Content="停止位" HorizontalAlignment="Left" Margin="219,176,0,0" VerticalAlignment="Top"/>
20         <Button x:Name="btn_port" Content="打開串口" HorizontalAlignment="Left" Margin="440,176,0,0" VerticalAlignment="Top" Width="75" Click="btn_port_Click"/>
21         <Button x:Name="btn_StartService" Content="開啟服務" HorizontalAlignment="Left" Margin="440,228,0,0" VerticalAlignment="Top" Width="75" Click="btn_start_Click"/>
22         <Button x:Name="btn_StopService" Content="關閉服務" HorizontalAlignment="Left" Margin="559,228,0,0" VerticalAlignment="Top" Width="75" Click="btn_close_Click"/>
23         <TextBox x:Name="tb_serviceIP" HorizontalAlignment="Left" Height="23" Margin="92,228,0,0" TextWrapping="Wrap" Text="192.168.3.252" VerticalAlignment="Top" Width="120"/>
24         <TextBox x:Name="tb_servicePort" HorizontalAlignment="Left" Height="23" Margin="270,228,0,0" TextWrapping="Wrap" Text="2000" VerticalAlignment="Top" Width="120"/>
25         <Label Content="服務IP" HorizontalAlignment="Left" Margin="41,228,0,0" VerticalAlignment="Top"/>
26         <Label Content="服務端口" HorizontalAlignment="Left" Margin="213,228,0,0" VerticalAlignment="Top"/>
27 
28     </Grid>
29 </Window>

 

 

  1 using System;
  2 using System.IO.Ports;
  3 using System.Threading.Tasks;
  4 using System.Web.Http;
  5 using System.Web.Http.SelfHost;
  6 using System.Windows;
  7 using Modbus.Device;
  8 
  9 namespace ******
 10 {
 11     /// <summary>
 12     /// MainWindow.xaml 的交互邏輯
 13     /// </summary>
 14     public partial class MainWindow : Window
 15     {
 16         private static IModbusMaster master;
 17         private static SerialPort port;
 18         //寫線圈或寫寄存器數組
 19         private bool[] coilsBuffer;
 20         private ushort[] registerBuffer;
 21         //串口參數
 22         private string portName;
 23         private int baudRate;
 24         private Parity parity;
 25         private int dataBits;
 26         private StopBits stopBits;
 27 
 28         private HttpServer httpServer = null;
 29 
 30 
 31         public static MainWindow x;
 32         public MainWindow()
 33         {
 34             InitializeComponent();
 35             x = this;
 36         }
 37 
 38         #region 串口下拉列表刷新
 39         /// <summary>
 40         /// 刷新串口下拉列表顯示
 41         /// </summary>
 42         private void GetSerialLstTb1()
 43         {
 44             //清除cmb_portname顯示
 45             cmb_portname.SelectedIndex = -1;
 46             cmb_portname.Items.Clear();
 47             string[] serialLst = SerialPort.GetPortNames();
 48             if (serialLst.Length > 0)
 49             {
 50                 //取串口進行排序
 51                 Array.Sort(serialLst);
 52                 //將串口列表輸出到cmb_portname
 53                 for (int i = 0; i < serialLst.Length; i++)
 54                 {
 55                     cmb_portname.Items.Add(serialLst[i]);
 56                     cmb_portname.SelectedIndex = 0;
 57                 }
 58             }
 59         }
 60         /// <summary>
 61         /// 鼠標移入串口cmb觸發刷新
 62         /// </summary>
 63         /// <param name="sender"></param>
 64         /// <param name="e"></param>
 65         private void cmb_portname_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
 66         {
 67             GetSerialLstTb1();
 68         }
 69 
 70         #endregion
 71 
 72         /// <summary>
 73         /// 界面加載
 74         /// </summary>
 75         /// <param name="sender"></param>
 76         /// <param name="e"></param>
 77         private void Grid_Loaded(object sender, RoutedEventArgs e)
 78         {
 79             GetSerialLstTb1();
 80 
 81             cmb_baud.Items.Add("9600");
 82             cmb_baud.Items.Add("14400");
 83             cmb_baud.Items.Add("19200");
 84 
 85             cmb_parity.Items.Add("");
 86             cmb_parity.Items.Add("");
 87             cmb_parity.Items.Add("");
 88 
 89             cmb_dataBits.Items.Add("7");
 90             cmb_dataBits.Items.Add("8");
 91 
 92             cmb_stopBits.Items.Add("1");
 93             cmb_stopBits.Items.Add("2");
 94 
 95             cmb_portname.SelectedIndex = -1;
 96             cmb_baud.SelectedIndex = 0;
 97             cmb_parity.SelectedIndex = 2;
 98             cmb_dataBits.SelectedIndex = 1;
 99             cmb_stopBits.SelectedIndex = 0;
100         }
101         #region 串口配置
102         /// <summary>
103         /// 串口參數獲取
104         /// </summary>
105         /// <returns></返回串口配置參數>
106         private SerialPort InitSerialPortParameter()
107         {
108             if (cmb_portname.SelectedIndex < 0 || cmb_baud.SelectedIndex < 0 || cmb_parity.SelectedIndex < 0 || cmb_dataBits.SelectedIndex < 0 || cmb_stopBits.SelectedIndex < 0)
109             {
110                 MessageBox.Show("請選擇串口參數");
111                 return null;
112             }
113             else
114             {
115                 portName = cmb_portname.SelectedItem.ToString();
116                 baudRate = int.Parse(cmb_baud.SelectedItem.ToString());
117 
118                 switch (cmb_parity.SelectedItem.ToString())
119                 {
120                     case "":
121                         parity = Parity.Odd;
122                         break;
123                     case "":
124                         parity = Parity.Even;
125                         break;
126                     case "":
127                         parity = Parity.None;
128                         break;
129                     default:
130                         break;
131                 }
132                 dataBits = int.Parse(cmb_dataBits.SelectedItem.ToString());
133                 switch (cmb_stopBits.SelectedItem.ToString())
134                 {
135                     case "1":
136                         stopBits = StopBits.One;
137                         break;
138                     case "2":
139                         stopBits = StopBits.Two;
140                         break;
141                     default:
142                         break;
143                 }
144 
145                 port = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
146                 return port;
147 
148             }
149         }
150         #endregion
151         /// <summary>
152         /// 開閉端口
153         /// </summary>
154         /// <param name="sender"></param>
155         /// <param name="e"></param>
156         private void btn_port_Click(object sender, RoutedEventArgs e)
157         {
158             if (cmb_portname.Items.Count != 0)
159             {
160                 try
161                 {
162                     if ((string)btn_port.Content == "關閉串口")
163                     {
164                         btn_port.Content = "打開串口";
165                         port.Close();
166                     }
167                     else
168                     {
169                         InitSerialPortParameter();
170                         port.PortName = cmb_portname.SelectedItem.ToString();
171                         port.BaudRate = int.Parse(cmb_baud.SelectedValue.ToString());
172                         port.Open();
173                         btn_port.Content = "關閉串口";
174                     }
175                 }
176                 catch (Exception ex)
177                 {
178                     MessageBox.Show(ex.GetType().ToString());
179                     MessageBox.Show("端口被占用\n請選擇正確的端口", "錯誤");
180                 }
181             }
182             else
183             {
184                 MessageBox.Show("請選擇正確的端口", "錯誤");
185             }
186         }
187 
188         public class HttpServer
189         {
190             private HttpSelfHostServer server;
191 
192             public HttpServer(string ip, int port)
193             {
194                 var config = new HttpSelfHostConfiguration($"http:\\{ip}:{port}");
195                 config.MapHttpAttributeRoutes();
196                 config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{action}");
197                 server = new HttpSelfHostServer(config);
198             }
199 
200             public Task StartHttpServer()
201             {
202                 return server.OpenAsync();
203             }
204             public Task CloseHttpServer()
205             {
206                 return server.CloseAsync();
207             }
208         }
209 
210         private async void btn_start_Click(object sender, RoutedEventArgs e)
211         {
212             try
213             {
214                 httpServer = new HttpServer("\\" + tb_serviceIP.Text, int.Parse(tb_servicePort.Text));
215                 await httpServer.StartHttpServer();
216                 MessageBox.Show("服務開啟");
217             }
218             catch (Exception ex)
219             {
220                 MessageBox.Show("服務開啟失敗" + ex.Message);
221             }
222         }
223 
224         private async void btn_close_Click(object sender, RoutedEventArgs e)
225         {
226             try
227             {
228                 //httpServer = new HttpServer("\\" + tb_serviceIP.Text, int.Parse(tb_servicePort.Text));
229                 await httpServer.CloseHttpServer();
230                 MessageBox.Show("服務關閉");
231             }
232             catch (Exception ex)
233             {
234                 MessageBox.Show("服務關閉失敗" + ex.Message);
235             }
236         }
237 
238 
239         public class HomeController : ApiController
240         {
241             /// <summary>
242             /// 讀多個寄存器
243             /// http://192.168.3.252:2000/api/home/ReadKeepReg?slaveAdd=1&address=99&number=10
244             /// </summary>
245             /// <param name="slaveAdd">從站地址</param>
246             /// <param name="address">寄存器首地址</param>>
247             /// <param name="number">寄存器數量</param>
248             /// <returns></returns>
249             [HttpGet]
250             public IHttpActionResult ReadKeepReg(string slaveAdd, string address, string number)
251             {
252                 master = ModbusSerialMaster.CreateRtu(port);
253 
254                 string message = "";
255                 byte slaveAddress = byte.Parse(slaveAdd);
256                 ushort startAddress = ushort.Parse(address);
257                 ushort numberOfPoints = ushort.Parse(number);
258                 try
259                 {
260                     x.registerBuffer = master.ReadHoldingRegisters(slaveAddress, startAddress, numberOfPoints);
261                 }
262                 catch (Exception e)
263                 {
264                     MessageBox.Show(e.Message);
265                 }
266                 for (int i = 0; i < x.registerBuffer.Length; i++)
267                 {
268                     message = message + x.registerBuffer[i].ToString("X4") + " ";
269                 }
270                 //MessageBox.Show(message);
271                 return Json(message);
272             }
273             /// <summary>
274             /// 寫多個寄存器
275             /// http://192.168.3.252:2000/api/home/WriteKeepReg?slaveAdd=1&address=99&message=1234%201235%201236%201237
276             /// </summary>
277             /// <param name="slaveAdd">從站地址</param>
278             /// <param name="address">寄存器首地址</param>
279             /// <param name="message">信息,以空格分開。例1111 2222,就是寫兩個寄存器</param>
280             [HttpGet]
281             public void WriteKeepReg(string slaveAdd, string address, string message)
282             {
283                 master = ModbusSerialMaster.CreateRtu(port);
284                 byte slaveAddress = byte.Parse(slaveAdd);
285                 ushort startAddress = ushort.Parse(address);
286 
287                 slaveAddress = byte.Parse(slaveAdd);
288                 startAddress = ushort.Parse(address);
289 
290                 //轉化ushort數組
291                 string[] strarr = message.Split(' ');
292                 x.registerBuffer = new ushort[strarr.Length];
293                 for (int i = 0; i < strarr.Length; i++)
294                 {
295                     x.registerBuffer[i] = ushort.Parse(strarr[i], System.Globalization.NumberStyles.HexNumber);
296                 }
297                 master.WriteMultipleRegisters(slaveAddress, startAddress, x.registerBuffer);
298                 return;
299             }
300             /// <summary>
301             /// 讀多個線圈
302             /// http://192.168.3.252:2000/api/home/ReadMultiCoil?slaveAdd=1&address=99&number=10
303             /// </summary>
304             /// <param name="slaveAdd">從站地址</param>
305             /// <param name="address">線圈首地址</param>
306             /// <param name="number">線圈數量</param>
307             /// <returns></returns>
308             [HttpGet]
309             public IHttpActionResult ReadMultiCoil(string slaveAdd, string address, string number)
310             {
311                 master = ModbusSerialMaster.CreateRtu(port);
312 
313                 string message = ""; 
314                 byte slaveAddress = byte.Parse(slaveAdd);
315                 ushort startAddress = ushort.Parse(address);
316                 ushort numberOfPoints = ushort.Parse(number);
317                 try
318                 {
319                     x.coilsBuffer = master.ReadCoils(slaveAddress, startAddress, numberOfPoints);
320                 }
321                 catch (Exception e)
322                 {
323                     MessageBox.Show(e.Message);
324                 }
325                 for (int i = 0; i < x.coilsBuffer.Length; i++)
326                 {
327                     message = message + x.coilsBuffer[i] + " ";
328                 }
329                 //MessageBox.Show(message);
330                 return Json(message);
331             }
332             /// <summary>
333             /// 寫多個線圈
334             /// http://192.168.3.252:2000/api/home/WriteMultiCoil?slaveAdd=1&address=99&message=1%200%200%200%201%201%201%201
335             /// </summary>
336             /// <param name="slaveAdd">從站地址</param>
337             /// <param name="address">線圈首地址</param>
338             /// <param name="message">信息,空格分開</param>
339             [HttpGet]
340             public void WriteMultiCoil(string slaveAdd, string address, string message)
341             {
342                 master = ModbusSerialMaster.CreateRtu(port);
343                 byte slaveAddress = byte.Parse(slaveAdd);
344                 ushort startAddress = ushort.Parse(address);
345 
346                 slaveAddress = byte.Parse(slaveAdd);
347                 startAddress = ushort.Parse(address);
348 
349                 //轉化ushort數組
350                 string[] strarr = message.Split(' ');
351                 x.coilsBuffer = new bool[strarr.Length];
352                 for (int i = 0; i < strarr.Length; i++)
353                 {
354                     if (strarr[i] == "0")
355                     {
356                         x.coilsBuffer[i] = false;
357                     }
358                     else
359                     {
360                         x.coilsBuffer[i] = true;
361                     }
362                 }
363                 master.WriteMultipleCoilsAsync(slaveAddress, startAddress, x.coilsBuffer);
364                 return;
365 
366             }
367         }
368     }
369 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM