using Model; using Permission; using PlcCom; using Sunny.UI; using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Xml.Linq; namespace PlcUiForm { [FormDescriptionAttribute("窗体基类")] public class PlcBaseForm : UIForm { List PlcBindableComponents = new List(); private PlcBindingManager _plcBindingManager; private PlcContex plcContex; static IPlcComProtocol defaultPlcComProtocol; public readonly IPlcComProtocol PlcComProtocol; private int refreshInterval=100; private ControlConfigManager _configManager; [Category("PLC")] [Description("窗体内PLC寄存的读取周期,单位:ms")] public int RefreshInterval { get { return refreshInterval; } set { if (value < 10) value = 10; refreshInterval = value; } } public static void SetDefaultProtocol(IPlcComProtocol protocol) { if(protocol == null) throw new NullReferenceException(nameof(protocol)); defaultPlcComProtocol = protocol; } public PlcBaseForm(IPlcComProtocol protocol) { InitializeComponent(); if (protocol != null) { PlcComProtocol = protocol; plcContex = new PlcContex(protocol); } else { throw new Exception("请传入正确的通讯协议"); } OtherInitialize(); } public PlcBaseForm() { InitializeComponent(); if (defaultPlcComProtocol != null) { PlcComProtocol = defaultPlcComProtocol; plcContex = new PlcContex(defaultPlcComProtocol); } else { // throw new Exception("请使用SetDefaultProtocol方法传入正确的通讯协议"); } OtherInitialize(); } private void InitializeComponent() { this.SuspendLayout(); // // PlcBaseForm // this.ClientSize = new System.Drawing.Size(1221, 640); this.Name = "PlcBaseForm"; this.ZoomScaleRect = new System.Drawing.Rectangle(15, 15, 800, 480); this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.PlcComBaseForm_FormClosing); this.ResumeLayout(false); } void OtherInitialize() { Node.EnableDesignTimeValidation = IsDesignMode; } protected override void OnLoad(EventArgs e) { base.OnLoad(e); UpdatePermissions(); // 订阅权限变更事件 PermissionManager.OnPermissionLevelChanged += OnPermissionChanged; //Console.WriteLine(this.GetType().Name); //var plcBindableControls = this.Controls.Find("", true).OfType().ToList(); var plcBindableControls = FindSpecificControls.FindControlsByInterface(this); PlcBindableComponents.AddRange(plcBindableControls.Select(x => (IPlcBindableComponent)x).ToList()); PlcBindableComponents.AddRange(GetNonControlComponents()); foreach (var c in PlcBindableComponents) { if (c is IPlcInputComponent ic) { ic.OnWriteRegisterFailed += OnWriteRegisterFailed; ic.OnUserSettedValue += OnUserSettedValue; } if (c is IPlcOutputComponent oc) { oc.OnReadRegisterFailed += OnReadRegisterFailed; oc.OnUpdateComponentStateFailed += OnUpdateComponentStateFailed; } } _plcBindingManager = new PlcBindingManager(); _plcBindingManager.ReadOK += BindingManager_ReadOK; _plcBindingManager.ReadOneNodeFailed += BindingManager_ComponentReadFailed; _plcBindingManager.ReadMultiNodesFailed += BindingManager_ReadMultiNodesFailed; _plcBindingManager.ReadOverTime += BindingManager_ReadOverTime; _plcBindingManager.Bind(PlcBindableComponents, plcBindableControls); if (plcContex != null) _plcBindingManager.SetPlcContext(plcContex, RefreshInterval); // 或者根据控件动态判断 //查找带有最大最小值的控件 _configManager = new ControlConfigManager(this.GetType().Name); foreach (IWithMinMaxValue control in FindSpecificControls.FindControlsByInterface(this)) { control.MinMaxValueChanged += OnMinMaxValueChanged; var minMaxValue = _configManager.GetMinMax(Name, ((Control)control).Name); control.Minimum = minMaxValue.MinValue; control.Maximum = minMaxValue.MaxValue; } ////查找不是PLC地址绑定,但可以让用户操作的控件 //foreach (IMyOperationalControl control in FindSpecificControls.FindControlsByInterface(this)) //{ // control.OnUserOperatedControl += Control_OnUserOperatedControl; //} } private void UpdatePermissions() { SetIPermissionControlRequireLevel(); PermissionManager.ApplyPermissions(this); } private void SetIPermissionControlRequireLevel() { if (IsDesignMode) return; // 从对应权限文件加载授权列表(建议缓存,不要每次都读文件) var authorizedControls = PermissionManager.GetAuthorizedControlsForRole(AppSession.CurrentUser.PermissionLevel); // 构建查找集 var authSet = new HashSet( authorizedControls.Select(c => $"{c.Namespace}.{c.FormType}.{c.ControlName}") ); // 遍历所有字段 var fields = this.GetType().GetFields( BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); var assembly= this.GetType().Assembly; var ns = this.GetType().Namespace; var formTypeName = this.GetType().Name; foreach (var field in fields) { var fieldValue = field.GetValue(this); if (fieldValue is IActionableControl && fieldValue is IPermissionControl control) { if (control.RequiredPermissionLevel != PermissionLevel.无权限)//为无权限的控件不受权限控制 { var b = authSet.Contains($"{ns}.{formTypeName}.{((Control)control).Name}"); if (b) control.RequiredPermissionLevel = AppSession.CurrentUser.PermissionLevel; else control.RequiredPermissionLevel = PermissionLevel.开发者; } } } } //private void Control_OnUserOperatedControl(Control control, object state) //{ // string opMessage; // if (control is MyUiButton) // { // opMessage = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + $" {FormName}->“{control.Text}”按钮被点击"; // } // else // { // opMessage = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + $" {FormName}->“{control.Name}”被修改为:{state}"; // } // AppSession.UserOperations.Add(opMessage); //} private void OnPermissionChanged(PermissionLevel newPermissionLevel) { // 在 UI 线程上更新权限(防止跨线程异常) if (this.InvokeRequired) { this.Invoke(new Action(UpdatePermissions)); } else { UpdatePermissions(); } } private IEnumerable GetNonControlComponents() { var fields = this.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); foreach (var field in fields) { var value = field.GetValue(this); if (value is IPlcBindableComponent component && !(value is Control)) { yield return component; } } } //public static List ReadWriteErrors = new List(); //public static List UserOperations = new List(); public event Action ProtocollReadOK; private void BindingManager_ReadOK() { ProtocollReadOK?.Invoke(); } public event Action ProtocollReadOverTime; private void BindingManager_ReadOverTime() { ProtocollReadOverTime?.Invoke(); } private void BindingManager_ReadMultiNodesFailed(List arg1, uint arg2) { } private void BindingManager_ComponentReadFailed(IPlcBindableComponent plcBindableComponent, Node node, uint errorCode) { string err = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + $" {FormName}->{GetComponentName(plcBindableComponent)}->{node},读取失败,错误代码:0X{errorCode:X}"; AppSession.ReadWriteErrors.Add(err); } private void OnUpdateComponentStateFailed(IPlcBindableComponent plcBindableComponent,Node node, object state, string errMessage) { string name = plcBindableComponent is Control control ? control.Name : null; string err = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + $" {FormName}->{name},更新状态:\"{state}\"错误,错误信息:{errMessage}"; AppSession.ReadWriteErrors.Add(err); } private void OnUserSettedValue(IPlcBindableComponent plcBindableComponent,OperationType operationType, Node node,object oldValue, object newValue) { var controlName = GetComponentName(plcBindableComponent); OperationRecord operationRecord = new OperationRecord { Timestamp = DateTime.Now, OperationType = operationType, ControlName = $"{FormName}->{controlName}", OldValue= oldValue, NewValue = newValue, Detail = $"{node}->{newValue.ToString()}", User = AppSession.CurrentUser }; OperationRecordService.Log(operationRecord); string opMessage = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + $" {FormName}->{GetComponentName(plcBindableComponent)}->{node},人工变更为:{newValue.ToString()}"; AppSession.UserOperations.Add(opMessage); } private void OnReadRegisterFailed(IPlcBindableComponent plcBindableComponent, Node node, uint errorCode) { string err = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + $" {FormName}->{GetComponentName(plcBindableComponent)}->{node},读取失败,错误代码:0X{errorCode:X}"; AppSession.ReadWriteErrors.Add(err); } private void OnWriteRegisterFailed(IPlcBindableComponent plcBindableComponent, Node node, uint errorCode) { string err = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + $" {FormName}->{GetComponentName(plcBindableComponent)}->{node},写入失败,错误代码:0X{errorCode:X}"; AppSession.ReadWriteErrors.Add(err); } private void OnMinMaxValueChanged(Control control, double minimum, double maximum) { _configManager.SetMinMax(this.GetType().Name, control.Name, new MinMaxValue(minimum, maximum)); _configManager.SaveConfig(); } private void PlcComBaseForm_FormClosing(object sender, FormClosingEventArgs e) { PermissionManager.OnPermissionLevelChanged -= OnPermissionChanged; _plcBindingManager?.Stop(); FormEx.CloseAllChildForms(this); } // 窗体关闭时取消订阅,防止内存泄漏 protected override void OnFormClosed(FormClosedEventArgs e) { base.OnFormClosed(e); } public string FormName { get { if (!string.IsNullOrWhiteSpace(this.Text)) { return this.Text; } else if (!string.IsNullOrWhiteSpace(this.Name)) { return this.Name; } else { return this.GetType().Name; } } } public string GetTextOrName(Control control) { // 使用反射获取控件类型上的 Text 属性 PropertyInfo textProperty = control.GetType().GetProperty("Text", typeof(string)); if (textProperty != null) { // 控件有 Text 属性 string text = (string)textProperty.GetValue(control); if (string.IsNullOrWhiteSpace(text)) { return control.Name; } else { return text; } } else { // 控件没有 Text 属性,返回 Name return control.Name; } } public string GetComponentName(IPlcBindableComponent plcBindableComponent) { if (plcBindableComponent is Control control) { return control.Name; } else if(plcBindableComponent is IComponent component) { // 使用反射获取控件类型上的 NodeName 属性 PropertyInfo NodeNameProperty = component.GetType().GetProperty("NodeName", typeof(string)); if (NodeNameProperty != null && !string.IsNullOrWhiteSpace((string)NodeNameProperty.GetValue(component))) // 控件有 NodeName 属性,且值不为空 { return (string)NodeNameProperty.GetValue(component); } else { return component.Site?.Name; } } else return string.Empty; } public string GetNodeNameOrName(IPlcBindableComponent plcBindableComponent) { if (plcBindableComponent is Control || plcBindableComponent is Component) { // 使用反射获取控件类型上的 NodeName 属性 PropertyInfo nodeNameProperty = plcBindableComponent.GetType().GetProperty("NodeName", typeof(string)); if (nodeNameProperty != null && !string.IsNullOrWhiteSpace((string)nodeNameProperty.GetValue(plcBindableComponent))) // 控件有 NodeName 属性,且值不为空 { return (string)nodeNameProperty.GetValue(plcBindableComponent); } else { // 控件没有 NodeName 属性,返回 Name if (plcBindableComponent is Control control) return control.Name; else if (plcBindableComponent is IComponent component) return component.Site?.Name; else return string.Empty; } } else return string.Empty; } } }