PlcBaseForm.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. using Model;
  2. using Permission;
  3. using PlcCom;
  4. using Sunny.UI;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.ComponentModel;
  8. using System.Diagnostics;
  9. using System.Drawing;
  10. using System.Linq;
  11. using System.Reflection;
  12. using System.Text;
  13. using System.Threading.Tasks;
  14. using System.Windows.Forms;
  15. using System.Xml.Linq;
  16. namespace PlcUiForm
  17. {
  18. [FormDescriptionAttribute("窗体基类")]
  19. public class PlcBaseForm : UIForm
  20. {
  21. List<IPlcBindableComponent> PlcBindableComponents = new List<IPlcBindableComponent>();
  22. private PlcBindingManager _plcBindingManager;
  23. private PlcContex plcContex;
  24. static IPlcComProtocol defaultPlcComProtocol;
  25. public readonly IPlcComProtocol PlcComProtocol;
  26. private int refreshInterval=100;
  27. private ControlConfigManager _configManager;
  28. [Category("PLC")]
  29. [Description("窗体内PLC寄存的读取周期,单位:ms")]
  30. public int RefreshInterval
  31. {
  32. get { return refreshInterval; }
  33. set { if (value < 10) value = 10; refreshInterval = value; }
  34. }
  35. public static void SetDefaultProtocol(IPlcComProtocol protocol)
  36. {
  37. if(protocol == null)
  38. throw new NullReferenceException(nameof(protocol));
  39. defaultPlcComProtocol = protocol;
  40. }
  41. public PlcBaseForm(IPlcComProtocol protocol)
  42. {
  43. InitializeComponent();
  44. if (protocol != null)
  45. {
  46. PlcComProtocol = protocol;
  47. plcContex = new PlcContex(protocol);
  48. }
  49. else
  50. {
  51. throw new Exception("请传入正确的通讯协议");
  52. }
  53. OtherInitialize();
  54. }
  55. public PlcBaseForm()
  56. {
  57. InitializeComponent();
  58. if (defaultPlcComProtocol != null)
  59. {
  60. PlcComProtocol = defaultPlcComProtocol;
  61. plcContex = new PlcContex(defaultPlcComProtocol);
  62. }
  63. else
  64. {
  65. // throw new Exception("请使用SetDefaultProtocol方法传入正确的通讯协议");
  66. }
  67. OtherInitialize();
  68. }
  69. private void InitializeComponent()
  70. {
  71. this.SuspendLayout();
  72. //
  73. // PlcBaseForm
  74. //
  75. this.ClientSize = new System.Drawing.Size(1221, 640);
  76. this.Name = "PlcBaseForm";
  77. this.ZoomScaleRect = new System.Drawing.Rectangle(15, 15, 800, 480);
  78. this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.PlcComBaseForm_FormClosing);
  79. this.ResumeLayout(false);
  80. }
  81. void OtherInitialize()
  82. {
  83. Node.EnableDesignTimeValidation = IsDesignMode;
  84. }
  85. protected override void OnLoad(EventArgs e)
  86. {
  87. base.OnLoad(e);
  88. UpdatePermissions();
  89. // 订阅权限变更事件
  90. PermissionManager.OnPermissionLevelChanged += OnPermissionChanged;
  91. //Console.WriteLine(this.GetType().Name);
  92. //var plcBindableControls = this.Controls.Find("", true).OfType<IPlcUiControl>().ToList();
  93. var plcBindableControls = FindSpecificControls.FindControlsByInterface<IPlcUiControl>(this);
  94. PlcBindableComponents.AddRange(plcBindableControls.Select(x => (IPlcBindableComponent)x).ToList());
  95. PlcBindableComponents.AddRange(GetNonControlComponents());
  96. foreach (var c in PlcBindableComponents)
  97. {
  98. if (c is IPlcInputComponent ic)
  99. {
  100. ic.OnWriteRegisterFailed += OnWriteRegisterFailed;
  101. ic.OnUserSettedValue += OnUserSettedValue;
  102. }
  103. if (c is IPlcOutputComponent oc)
  104. {
  105. oc.OnReadRegisterFailed += OnReadRegisterFailed;
  106. oc.OnUpdateComponentStateFailed += OnUpdateComponentStateFailed;
  107. }
  108. }
  109. _plcBindingManager = new PlcBindingManager();
  110. _plcBindingManager.ReadOK += BindingManager_ReadOK;
  111. _plcBindingManager.ReadOneNodeFailed += BindingManager_ComponentReadFailed;
  112. _plcBindingManager.ReadMultiNodesFailed += BindingManager_ReadMultiNodesFailed;
  113. _plcBindingManager.ReadOverTime += BindingManager_ReadOverTime;
  114. _plcBindingManager.Bind(PlcBindableComponents, plcBindableControls);
  115. if (plcContex != null)
  116. _plcBindingManager.SetPlcContext(plcContex, RefreshInterval); // 或者根据控件动态判断
  117. //查找带有最大最小值的控件
  118. _configManager = new ControlConfigManager(this.GetType().Name);
  119. foreach (IWithMinMaxValue control in FindSpecificControls.FindControlsByInterface<IWithMinMaxValue>(this))
  120. {
  121. control.MinMaxValueChanged += OnMinMaxValueChanged;
  122. var minMaxValue = _configManager.GetMinMax(Name, ((Control)control).Name);
  123. control.Minimum = minMaxValue.MinValue;
  124. control.Maximum = minMaxValue.MaxValue;
  125. }
  126. ////查找不是PLC地址绑定,但可以让用户操作的控件
  127. //foreach (IMyOperationalControl control in FindSpecificControls.FindControlsByInterface<IMyOperationalControl>(this))
  128. //{
  129. // control.OnUserOperatedControl += Control_OnUserOperatedControl;
  130. //}
  131. }
  132. private void UpdatePermissions()
  133. {
  134. SetIPermissionControlRequireLevel();
  135. PermissionManager.ApplyPermissions(this);
  136. }
  137. private void SetIPermissionControlRequireLevel()
  138. {
  139. if (IsDesignMode)
  140. return;
  141. // 从对应权限文件加载授权列表(建议缓存,不要每次都读文件)
  142. var authorizedControls = PermissionManager.GetAuthorizedControlsForRole(AppSession.CurrentUser.PermissionLevel);
  143. // 构建查找集
  144. var authSet = new HashSet<string>(
  145. authorizedControls.Select(c => $"{c.Namespace}.{c.FormType}.{c.ControlName}")
  146. );
  147. // 遍历所有字段
  148. var fields = this.GetType().GetFields(
  149. BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
  150. var assembly= this.GetType().Assembly;
  151. var ns = this.GetType().Namespace;
  152. var formTypeName = this.GetType().Name;
  153. foreach (var field in fields)
  154. {
  155. var fieldValue = field.GetValue(this);
  156. if (fieldValue is IActionableControl && fieldValue is IPermissionControl control)
  157. {
  158. if (control.RequiredPermissionLevel != PermissionLevel.无权限)//为无权限的控件不受权限控制
  159. {
  160. var b = authSet.Contains($"{ns}.{formTypeName}.{((Control)control).Name}");
  161. if (b)
  162. control.RequiredPermissionLevel = AppSession.CurrentUser.PermissionLevel;
  163. else
  164. control.RequiredPermissionLevel = PermissionLevel.开发者;
  165. }
  166. }
  167. }
  168. }
  169. //private void Control_OnUserOperatedControl(Control control, object state)
  170. //{
  171. // string opMessage;
  172. // if (control is MyUiButton)
  173. // {
  174. // opMessage = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + $" {FormName}->“{control.Text}”按钮被点击";
  175. // }
  176. // else
  177. // {
  178. // opMessage = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + $" {FormName}->“{control.Name}”被修改为:{state}";
  179. // }
  180. // AppSession.UserOperations.Add(opMessage);
  181. //}
  182. private void OnPermissionChanged(PermissionLevel newPermissionLevel)
  183. {
  184. // 在 UI 线程上更新权限(防止跨线程异常)
  185. if (this.InvokeRequired)
  186. {
  187. this.Invoke(new Action(UpdatePermissions));
  188. }
  189. else
  190. {
  191. UpdatePermissions();
  192. }
  193. }
  194. private IEnumerable<IPlcBindableComponent> GetNonControlComponents()
  195. {
  196. var fields = this.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
  197. foreach (var field in fields)
  198. {
  199. var value = field.GetValue(this);
  200. if (value is IPlcBindableComponent component && !(value is Control))
  201. {
  202. yield return component;
  203. }
  204. }
  205. }
  206. //public static List<string> ReadWriteErrors = new List<string>();
  207. //public static List<string> UserOperations = new List<string>();
  208. public event Action ProtocollReadOK;
  209. private void BindingManager_ReadOK()
  210. {
  211. ProtocollReadOK?.Invoke();
  212. }
  213. public event Action ProtocollReadOverTime;
  214. private void BindingManager_ReadOverTime()
  215. {
  216. ProtocollReadOverTime?.Invoke();
  217. }
  218. private void BindingManager_ReadMultiNodesFailed(List<string> arg1, uint arg2)
  219. {
  220. }
  221. private void BindingManager_ComponentReadFailed(IPlcBindableComponent plcBindableComponent, Node node, uint errorCode)
  222. {
  223. string err = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + $" {FormName}->{GetComponentName(plcBindableComponent)}->{node},读取失败,错误代码:0X{errorCode:X}";
  224. AppSession.ReadWriteErrors.Add(err);
  225. }
  226. private void OnUpdateComponentStateFailed(IPlcBindableComponent plcBindableComponent,Node node, object state, string errMessage)
  227. {
  228. string name = plcBindableComponent is Control control ? control.Name : null;
  229. string err = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + $" {FormName}->{name},更新状态:\"{state}\"错误,错误信息:{errMessage}";
  230. AppSession.ReadWriteErrors.Add(err);
  231. }
  232. private void OnUserSettedValue(IPlcBindableComponent plcBindableComponent,OperationType operationType, Node node,object oldValue, object newValue)
  233. {
  234. var controlName = GetComponentName(plcBindableComponent);
  235. OperationRecord operationRecord = new OperationRecord
  236. {
  237. Timestamp = DateTime.Now,
  238. OperationType = operationType,
  239. ControlName = $"{FormName}->{controlName}",
  240. OldValue= oldValue,
  241. NewValue = newValue,
  242. Detail = $"{node}->{newValue.ToString()}",
  243. User = AppSession.CurrentUser
  244. };
  245. OperationRecordService.Log(operationRecord);
  246. string opMessage = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + $" {FormName}->{GetComponentName(plcBindableComponent)}->{node},人工变更为:{newValue.ToString()}";
  247. AppSession.UserOperations.Add(opMessage);
  248. }
  249. private void OnReadRegisterFailed(IPlcBindableComponent plcBindableComponent, Node node, uint errorCode)
  250. {
  251. string err = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + $" {FormName}->{GetComponentName(plcBindableComponent)}->{node},读取失败,错误代码:0X{errorCode:X}";
  252. AppSession.ReadWriteErrors.Add(err);
  253. }
  254. private void OnWriteRegisterFailed(IPlcBindableComponent plcBindableComponent, Node node, uint errorCode)
  255. {
  256. string err = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + $" {FormName}->{GetComponentName(plcBindableComponent)}->{node},写入失败,错误代码:0X{errorCode:X}";
  257. AppSession.ReadWriteErrors.Add(err);
  258. }
  259. private void OnMinMaxValueChanged(Control control, double minimum, double maximum)
  260. {
  261. _configManager.SetMinMax(this.GetType().Name, control.Name, new MinMaxValue(minimum, maximum));
  262. _configManager.SaveConfig();
  263. }
  264. private void PlcComBaseForm_FormClosing(object sender, FormClosingEventArgs e)
  265. {
  266. PermissionManager.OnPermissionLevelChanged -= OnPermissionChanged;
  267. _plcBindingManager?.Stop();
  268. FormEx.CloseAllChildForms(this);
  269. }
  270. // 窗体关闭时取消订阅,防止内存泄漏
  271. protected override void OnFormClosed(FormClosedEventArgs e)
  272. {
  273. base.OnFormClosed(e);
  274. }
  275. public string FormName {
  276. get
  277. {
  278. if (!string.IsNullOrWhiteSpace(this.Text))
  279. {
  280. return this.Text;
  281. }
  282. else if (!string.IsNullOrWhiteSpace(this.Name))
  283. {
  284. return this.Name;
  285. }
  286. else
  287. {
  288. return this.GetType().Name;
  289. }
  290. }
  291. }
  292. public string GetTextOrName(Control control)
  293. {
  294. // 使用反射获取控件类型上的 Text 属性
  295. PropertyInfo textProperty = control.GetType().GetProperty("Text", typeof(string));
  296. if (textProperty != null)
  297. {
  298. // 控件有 Text 属性
  299. string text = (string)textProperty.GetValue(control);
  300. if (string.IsNullOrWhiteSpace(text))
  301. {
  302. return control.Name;
  303. }
  304. else
  305. {
  306. return text;
  307. }
  308. }
  309. else
  310. {
  311. // 控件没有 Text 属性,返回 Name
  312. return control.Name;
  313. }
  314. }
  315. public string GetComponentName(IPlcBindableComponent plcBindableComponent)
  316. {
  317. if (plcBindableComponent is Control control)
  318. {
  319. return control.Name;
  320. }
  321. else if(plcBindableComponent is IComponent component)
  322. {
  323. // 使用反射获取控件类型上的 NodeName 属性
  324. PropertyInfo NodeNameProperty = component.GetType().GetProperty("NodeName", typeof(string));
  325. if (NodeNameProperty != null && !string.IsNullOrWhiteSpace((string)NodeNameProperty.GetValue(component))) // 控件有 NodeName 属性,且值不为空
  326. {
  327. return (string)NodeNameProperty.GetValue(component);
  328. }
  329. else
  330. {
  331. return component.Site?.Name;
  332. }
  333. }
  334. else
  335. return string.Empty;
  336. }
  337. public string GetNodeNameOrName(IPlcBindableComponent plcBindableComponent)
  338. {
  339. if (plcBindableComponent is Control || plcBindableComponent is Component)
  340. {
  341. // 使用反射获取控件类型上的 NodeName 属性
  342. PropertyInfo nodeNameProperty = plcBindableComponent.GetType().GetProperty("NodeName", typeof(string));
  343. if (nodeNameProperty != null && !string.IsNullOrWhiteSpace((string)nodeNameProperty.GetValue(plcBindableComponent))) // 控件有 NodeName 属性,且值不为空
  344. {
  345. return (string)nodeNameProperty.GetValue(plcBindableComponent);
  346. }
  347. else
  348. {
  349. // 控件没有 NodeName 属性,返回 Name
  350. if (plcBindableComponent is Control control)
  351. return control.Name;
  352. else if (plcBindableComponent is IComponent component)
  353. return component.Site?.Name;
  354. else
  355. return string.Empty;
  356. }
  357. }
  358. else
  359. return string.Empty;
  360. }
  361. }
  362. }