• C#上位机与三菱PLC的通信11---开发自己的通讯工具软件(WPF版)


    1、先看颜值

    2、开始干

    1、创建项目

    2、引入前面的通讯库

     创建目录将前面生成的通讯库dll文件复制到项目的目录

    本项目引入dll文件

     

     3、创建命令基类

    RelayCommand.cs代码

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading.Tasks;
    6. using System.Windows.Input;
    7. namespace MitsubishiMcToolWPF.Common
    8. {
    9. ///
    10. /// 命令实现类
    11. ///
    12. public class RelayCommand : ICommand
    13. {
    14. public event EventHandler CanExecuteChanged;
    15. ///
    16. /// 要执行的操作
    17. ///
    18. private Action<object> executeActions;
    19. ///
    20. /// 是否可以执行的委托
    21. ///
    22. private Func<object, bool> canExecuteFunc;
    23. ///
    24. /// 构造函数 无参构造
    25. ///
    26. public RelayCommand() { }
    27. ///
    28. /// 通过执行的委托构造
    29. ///
    30. ///
    31. public RelayCommand(Action<object> execute) : this(execute, null)
    32. {
    33. }
    34. ///
    35. /// 通过执行的操作与是否可执行的委托
    36. ///
    37. /// 要执行的操作
    38. /// 是否可以被执行
    39. public RelayCommand(Action<object> execute, Func<object, bool> canExecute)
    40. {
    41. this.executeActions = execute;
    42. this.canExecuteFunc = canExecute;
    43. }
    44. ///
    45. /// 命令是否可以执行
    46. ///
    47. ///
    48. ///
    49. public bool CanExecute(object parameter)
    50. {
    51. if (canExecuteFunc != null)
    52. return this.canExecuteFunc(parameter);
    53. else
    54. return true;
    55. }
    56. ///
    57. /// 要执行的操作
    58. ///
    59. ///
    60. public void Execute(object parameter)
    61. {
    62. if (executeActions == null)
    63. return;
    64. this.executeActions(parameter);
    65. }
    66. ///
    67. /// 执行CanExecuteChanged事件
    68. ///
    69. public void OnCanExecuteChanged()
    70. {
    71. this.CanExecuteChanged?.Invoke(this, new EventArgs());
    72. }
    73. }
    74. }

     4、创建PLC实体类

    PLCMemoryModel.cs代码

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading.Tasks;
    6. namespace MitsubishiMcToolWPF.Model
    7. {
    8. ///
    9. /// PLC内存模型类
    10. ///
    11. public class PLCMemoryModel
    12. {
    13. ///
    14. /// 存储区
    15. ///
    16. public string Area { get; set; } = "";
    17. ///
    18. /// 地址
    19. ///
    20. public string Address { get; set; } = "";
    21. ///
    22. /// 数据类型
    23. ///
    24. public string DataType { get; set; } = "";
    25. ///
    26. /// 读取或写入数据
    27. ///
    28. public string Count { get; set; } = "";
    29. }
    30. }

     5、添加资源字典,即样式文件

    CommonResource.xaml完整代码

    1. "ComboBox.Static.Background" EndPoint="0,1" StartPoint="0,0">
    2. "White" Offset="0"/>
    3. "#FFE4E4E4" Offset="1"/>
    4. "ComboBox.Static.Border" Color="#FF105190"/>

     

     6、创建viewmodel

    viewmodel是视图模型,wpf采用的是mvvm的渲染模式,即控件绑定对象的属性,属性发生更改时,驱动控件的数据显示,而控件动作是由命令command执行

    1、模型基类

    ViewModelBase.cs代码

    1. using System;
    2. using System.Collections.Generic;
    3. using System.ComponentModel;
    4. using System.Linq;
    5. using System.Runtime.CompilerServices;
    6. using System.Text;
    7. using System.Threading.Tasks;
    8. using static MitsubishiMcToolWPF.MsgBoxWindow;
    9. namespace MitsubishiMcToolWPF.ViewModel
    10. {
    11. ///
    12. /// 视图模型基类
    13. ///
    14. public class ViewModelBase : INotifyPropertyChanged
    15. {
    16. ///
    17. /// 属性值发生更改时触发
    18. ///
    19. public event PropertyChangedEventHandler PropertyChanged;
    20. ///
    21. /// 执行更改
    22. /// C#5.0中的新特性CallerMemberName
    23. ///
    24. ///
    25. protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
    26. {
    27. if (PropertyChanged != null)
    28. {
    29. PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
    30. }
    31. }
    32. ///
    33. /// 成功消息提示
    34. ///
    35. ///
    36. ///
    37. ///
    38. public void ShowMessage(string message, string title = "提示", CustomMessageBoxButton buttons = CustomMessageBoxButton.OK)
    39. {
    40. Show(message, title, buttons, CustomMessageBoxIcon.Infomation);
    41. }
    42. ///
    43. /// 错误消息框
    44. ///
    45. ///
    46. ///
    47. ///
    48. public void ShowError(string message, string title = "错误", CustomMessageBoxButton buttons = CustomMessageBoxButton.OK)
    49. {
    50. Show(message, title, buttons, CustomMessageBoxIcon.Error);
    51. }
    52. ///
    53. /// 询问消息框
    54. ///
    55. ///
    56. ///
    57. ///
    58. ///
    59. public CustomMessageBoxResult ShowQuestion(string message, string title = "询问", CustomMessageBoxButton buttons = CustomMessageBoxButton.OKCancel)
    60. {
    61. return Show(message, title, buttons, CustomMessageBoxIcon.Question);
    62. }
    63. }
    64. }

     2、主页模型mainviewmodel

    MainViewModel.cs代码

     

    1. using Mitsubishi.Communication.MC.Mitsubishi;
    2. using MitsubishiMcToolWPF.Common;
    3. using MitsubishiMcToolWPF.Model;
    4. using System;
    5. using System.Collections.Generic;
    6. using System.Linq;
    7. using System.Text;
    8. using System.Threading.Tasks;
    9. using System.Windows.Input;
    10. namespace MitsubishiMcToolWPF.ViewModel
    11. {
    12. ///
    13. /// 视图模型
    14. ///
    15. public class MainViewModel : ViewModelBase
    16. {
    17. ///
    18. /// mctcp对象
    19. ///
    20. A3E mctcp;
    21. #region 属性
    22. PLCMemoryModel readPLCModel = SetInitModel();
    23. ///
    24. /// 读取PLC
    25. ///
    26. public PLCMemoryModel ReadPLCModel
    27. {
    28. get { return readPLCModel; }
    29. set
    30. {
    31. readPLCModel = ReadPLCModel;
    32. OnPropertyChanged();//属性通知
    33. }
    34. }
    35. PLCMemoryModel writePLCModel = SetInitModel();
    36. ///
    37. /// 写入PLC
    38. ///
    39. public PLCMemoryModel WritePLCModel
    40. {
    41. get { return writePLCModel; }
    42. set
    43. {
    44. writePLCModel = WritePLCModel;
    45. OnPropertyChanged();
    46. }
    47. }
    48. ///
    49. /// 初始化页面参数
    50. ///
    51. ///
    52. private static PLCMemoryModel SetInitModel()
    53. {
    54. PLCMemoryModel model = new PLCMemoryModel();
    55. model.Area = "D";
    56. model.DataType = "short";
    57. model.Address = "100";
    58. model.Count = "1";
    59. return model;
    60. }
    61. private string hostName = "192.168.1.7";
    62. ///
    63. /// PLC地址
    64. ///
    65. public string HostName
    66. {
    67. get
    68. {
    69. return hostName;
    70. }
    71. set
    72. {
    73. hostName = value;
    74. }
    75. }
    76. private string hostPort = "6000";
    77. ///
    78. /// PLC端口
    79. ///
    80. public string HostPort
    81. {
    82. get { return hostPort; }
    83. set
    84. {
    85. hostPort = value;
    86. }
    87. }
    88. ///
    89. /// 存储区下拉框数据源
    90. ///
    91. public List<string> CboCustTypes
    92. {
    93. get
    94. {
    95. return new List<string>() { "D", "X", "Y", "M", "R", "S", "TS" };
    96. }
    97. }
    98. ///
    99. /// 数据类型
    100. ///
    101. public List<string> CboCustDatas
    102. {
    103. get
    104. {
    105. return new List<string>() { "short", "float", "bool" };
    106. }
    107. }
    108. private string readWords = "";
    109. ///
    110. /// 读数结果
    111. ///
    112. public string ReadWords
    113. {
    114. get { return readWords; }
    115. set
    116. {
    117. readWords = value;
    118. OnPropertyChanged();
    119. }
    120. }
    121. private string writeWords = "";
    122. ///
    123. /// 写入数据
    124. ///
    125. public string WriteWords
    126. {
    127. get { return writeWords; }
    128. set
    129. {
    130. writeWords = value;
    131. OnPropertyChanged();
    132. }
    133. }
    134. private string connectWords = "当前未连接";
    135. ///
    136. /// 连接状态
    137. ///
    138. public string ConnectWords
    139. {
    140. get { return connectWords; }
    141. set
    142. {
    143. connectWords = value;
    144. OnPropertyChanged();
    145. }
    146. }
    147. #endregion
    148. #region 命令
    149. ///
    150. ///
    151. ///连接按钮的命令
    152. ///
    153. public ICommand ConnCommand
    154. {
    155. get
    156. {
    157. return new RelayCommand(o =>
    158. {
    159. var name = HostName;
    160. var port = HostPort;
    161. mctcp = new A3E(name, short.Parse(port));// 创建连接
    162. var result = mctcp.Connect();// 开始连接PLC
    163. if (!result.IsSuccessed)
    164. {
    165. ShowError(result.Message, "错误");
    166. return;
    167. }
    168. ConnectWords = "PLC已连接";
    169. ShowMessage("PLC连接成功", "提示");
    170. });
    171. }
    172. }
    173. ///
    174. ///
    175. ///断开按钮的命令
    176. ///
    177. public ICommand CloseCommand
    178. {
    179. get
    180. {
    181. return new RelayCommand(o =>
    182. {
    183. if (mctcp == null)
    184. {
    185. ShowError("PLC没有连接,不能断开", "错误");
    186. return;
    187. }
    188. mctcp = null;
    189. ConnectWords = "PLC已断开";
    190. });
    191. }
    192. }
    193. ///
    194. ///
    195. /// 读取按钮的命令
    196. ///
    197. public ICommand ReadCommand
    198. {
    199. get
    200. {
    201. return new RelayCommand(o =>
    202. {
    203. ReadWords = "";
    204. var d = ReadPLCModel;
    205. if (mctcp == null)
    206. {
    207. ShowError("PLC未连接,不能读取数据", "错误");
    208. return;
    209. }
    210. //内存地址
    211. string plcAddr = ReadPLCModel.Area + ReadPLCModel.Address;
    212. //读取数量
    213. short readCount = short.Parse(ReadPLCModel.Count);
    214. //数据类型
    215. string dataType = ReadPLCModel.DataType;
    216. switch (dataType)
    217. {
    218. case "short":
    219. var datas2 = mctcp.Read<short>(plcAddr, readCount);
    220. if (!datas2.IsSuccessed)
    221. {
    222. ShowMessage(datas2.Message, "提示");
    223. return;
    224. }
    225. ReadWords = string.Join(",", datas2.Datas);
    226. break;
    227. case "float":
    228. var datas3 = mctcp.Read<float>(plcAddr, readCount);
    229. if (!datas3.IsSuccessed)
    230. {
    231. ShowMessage(datas3.Message, "提示");
    232. return;
    233. }
    234. ReadWords = string.Join(",", datas3.Datas);
    235. break;
    236. case "bool":
    237. var datas4 = mctcp.Read<bool>(plcAddr, readCount);
    238. if (!datas4.IsSuccessed)
    239. {
    240. ShowMessage(datas4.Message, "提示");
    241. return;
    242. }
    243. ReadWords = string.Join(",", datas4.Datas);
    244. break;
    245. }
    246. });
    247. }
    248. }
    249. ///
    250. ///
    251. /// 写入按钮的命令处理
    252. ///
    253. public ICommand WriteCommand
    254. {
    255. get
    256. {
    257. return new RelayCommand(o =>
    258. {
    259. if (mctcp == null)
    260. {
    261. ShowError("PLC未连接,不能写入数据", "错误");
    262. return;
    263. }
    264. var d = WritePLCModel;
    265. //内存地址
    266. string plcAddr = WritePLCModel.Area + WritePLCModel.Address;
    267. //写入数量
    268. ushort writeCount = ushort.Parse(WritePLCModel.Count);
    269. //数据类型
    270. string dataType = WritePLCModel.DataType;
    271. //实际数量
    272. string objWriteVals = WriteWords;
    273. ushort objWCount = (ushort)objWriteVals.Split(',').Length;
    274. //实际数量与要求数量不一致,不允许操作
    275. if (writeCount != objWCount)
    276. {
    277. ShowError("写入值的数量不正确!");
    278. return;
    279. }
    280. List<string> vals = objWriteVals.Split(',').ToList();
    281. switch (dataType)
    282. {
    283. case "short":
    284. //实际数值转换成list集合 short类型
    285. List<short> objshort = new List<short>();
    286. vals.ForEach((x) =>
    287. {
    288. objshort.Add(short.Parse(x));
    289. });
    290. var finish2 = mctcp.Write<short>(objshort, plcAddr);
    291. if (finish2.IsSuccessed)
    292. {
    293. ShowMessage("写入成功", "提示");
    294. }
    295. break;
    296. case "float":
    297. //实际数值转换成list集合 float
    298. List<float> objfloat = new List<float>();
    299. vals.ForEach((x) =>
    300. {
    301. objfloat.Add(float.Parse(x));
    302. });
    303. var finish3 = mctcp.Write<float>(objfloat, plcAddr);
    304. if (finish3.IsSuccessed)
    305. {
    306. ShowMessage("写入成功", "提示");
    307. }
    308. break;
    309. case "bool":
    310. //实际数值转换成list集合bool
    311. List<bool> objbool = new List<bool>();
    312. vals.ForEach((x) =>
    313. {
    314. if (x == "1")
    315. {
    316. objbool.Add(true);
    317. }
    318. else
    319. {
    320. objbool.Add(false);
    321. }
    322. });
    323. var finish4 = mctcp.Write<bool>(objbool, plcAddr);
    324. if (finish4.IsSuccessed)
    325. {
    326. ShowMessage("写入成功", "提示");
    327. }
    328. break;
    329. }
    330. });
    331. }
    332. }
    333. #endregion
    334. }
    335. }

    7、主界面布局

    MainWindow.xaml完整代码

    1. "MitsubishiMcToolWPF.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:MitsubishiMcToolWPF.ViewModel"
    7. mc:Ignorable="d"
    8. FontSize="13" FontFamily="Microsoft YaHei" ResizeMode="CanMinimize" Title="三菱PLC通讯工具" FontWeight="ExtraLight" WindowStartupLocation="CenterScreen" Height="450" Width="880" Name="mainWin" >
    9. "true">
    10. "62"/>
    11. "0" Background="BlanchedAlmond">
    12. "100" />
    13. "0" Source="/imgs/log1.png" Width="60" Height="60" Margin="20,0" />
    14. "1" Text="三菱MC协议通讯调试助手(Qna-3E模式)" FontSize="23" VerticalAlignment="Center" FontWeight="Bold" />
    15. "1" Margin="0 10 0 0">
    16. "200"/>
    17. "4"/>
    18. "0" Margin="20">
    19. "PLC地址"/>
    20. "30" VerticalContentAlignment="Center" Padding="5,0" Margin="0,10" Text="{Binding HostName}" />
    21. "端口号" Margin="0,10,0,0"/>
    22. "30" VerticalContentAlignment="Center" Padding="5,0" Margin="0,10" Text="{Binding HostPort}" />
    23. "{Binding ConnectWords,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Margin="0,10,0,0" Foreground="Blue"/>
    24. "1" Background="#4490AC" Width="2" Height="450" Margin="0 -11 0 0">
    25. "2">
    26. "60"/>
    27. "80"/>
    28. "30"/>
    29. "60"/>
    30. "80"/>
    31. "Horizontal" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Margin="10">
    32. "存储区:" Style="{StaticResource txtTextBlockStyle}" Margin="0 0 0 0" >
    33. "{Binding CboCustTypes}" SelectedValue="{Binding ReadPLCModel.Area}" Width="70" Style="{StaticResource cboStyle}">
    34. "数据类型:" Style="{StaticResource txtTextBlockStyle}" >
    35. "{Binding CboCustDatas}" SelectedValue="{Binding ReadPLCModel.DataType}" Width="70" Style="{StaticResource cboStyle}">
    36. "地址:" Style="{StaticResource txtTextBlockStyle}" >
    37. "{Binding ReadPLCModel.Address}" Width="100" Style="{StaticResource txtTextBoxStyle}" />
    38. "数量:" Style="{StaticResource txtTextBlockStyle}" >
    39. "{Binding ReadPLCModel.Count}" Width="60" Style="{StaticResource txtTextBoxStyle}" />
    40. "Vertical" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Margin="10">
    41. "读取结果(多个数据之间用逗号隔开)" Style="{StaticResource txtTextBlockStyle}" Margin="0 3 0 9">
    42. "{Binding ReadWords,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Width="616" Style="{StaticResource txtTextBoxStyle}" Margin="-23,0,0,0" />
    43. "Horizontal" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Background="#4490AC" Width="706" Height="2" Margin="-1,0,0,0">
    44. "Horizontal" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" Margin="10">
    45. "存储区:" Style="{StaticResource txtTextBlockStyle}" Margin="0 0 0 0">
    46. "{Binding CboCustTypes}" SelectedValue="{Binding WritePLCModel.Area }" Width="70" Style="{StaticResource cboStyle}">
    47. "数据类型:" Style="{StaticResource txtTextBlockStyle}" >
    48. "{Binding CboCustDatas}" SelectedValue="{Binding WritePLCModel.DataType }" Width="70" Style="{StaticResource cboStyle}">
    49. "地址:" Style="{StaticResource txtTextBlockStyle}" >
    50. "{Binding WritePLCModel.Address }" Width="100" Style="{StaticResource txtTextBoxStyle}" />
    51. "数量:" Style="{StaticResource txtTextBlockStyle}" >
    52. "{Binding WritePLCModel.Count}" Width="60" Style="{StaticResource txtTextBoxStyle}" />
    53. "Vertical" Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" Margin="10">
    54. "写入数据(多个数据之间用逗号隔开)" Style="{StaticResource txtTextBlockStyle}" Margin="0 3 0 9" >
    55. "{Binding WriteWords ,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Width="616" Style="{StaticResource txtTextBoxStyle}" Margin="-23,0,0,0" />

     

     8、启动MC服务器

     9、异常错误处理

     

    10、操作测试

    1、连接PLC

    2、读取数据,short,float,bool

     

    3、写入数据,short,float

     写入short

    写入float

     

    3、小结

     

    走过路过不要错过,点赞关注收藏又圈粉,共同致富。

    欢迎抄袭,复制,分享,转载,学习,截图..............

    欢迎抄袭,复制,分享,转载,学习,截图..............

  • 相关阅读:
    Android开发 期末复习
    React - ref 命令为什么代替父子组件的数据传递
    CRM软件系统维护客户的主要方法
    IDEA中创建Web工程流程
    SpringBoot概念、创建和运行
    Instance Tunnel 使用
    做web自动化测试遇到Chrome浏览器老是自动更新,怎么办 ? 这里提供两个解决办法 。
    用 Rust 编写 eBPF/XDP 负载均衡器
    计算机网络(自顶向下方法)-链路层和局域网
    SSE图像算法优化系列三十一:RGB2HSL/RGB2HSV及HSL2RGB/HSV2RGB的指令集优化-上。
  • 原文地址:https://blog.csdn.net/hqwest/article/details/136250638