Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Winform not work perfeclty #202

Open
franklupo opened this issue Jun 7, 2021 · 11 comments
Open

Winform not work perfeclty #202

franklupo opened this issue Jun 7, 2021 · 11 comments

Comments

@franklupo
Copy link

Hi,
excellent work. It happens in winform that the ViewModel sometimes does not write to the PLC

Best regards

@ismdiego
Copy link

Hi Frank,

Take a look at #190 because I think you have the same problem I had... and sometimes still have...

WPF and Windows Forms should be doing different things with view models. My problem was that the ViewModel was "frozen" and OPCUA lib did not even start. When I subscribe for some events in the view model, then "magically" the ViewModel starts to work and communicate.

I think this is related to async code not executing or maybe the main task is paused. Sometimes, my ViewModels run without doing anything special to them. So for me this is still an open issue (not related to this library, I think, but definitely a show stopper if you face it).

@franklupo
Copy link
Author

Hi,
and how do i solve it?

best regards

@ismdiego
Copy link

Well, in my case the VM did not read and write. If your problem is only related to the VM cannot write, then maybe is a Thread problem. You can try marshalling to the current Thread to write the values to the VM (how... it depends on your code).

If the problem is related to no reading and no writing, then you must try to create a Task/Thread for that ViewModel. I am not still 100% sure of how this works, but in my case I could accomplish that by subscribing to a PropertyChanged event of the VM from the View.

As I am using ReactiveUI MVVM Framework, this translates in using a WhenAnyValue method with whatever VM property. Maybe this it not your case, how are you using the VM in WinForms?

@awcullen
Copy link
Contributor

Could you share some code of the ViewModel that is not working?

@franklupo
Copy link
Author

HI,

Model

  [Subscription(endpointUrl: FinalSawHelper.Opc.REQUEST_URL, publishingInterval: 500, keepAliveCount: 20)]
    public class FinalSawOpcViewModel : SubscriptionBase
    {
        public FinalSawOpcViewModel() : base() { }
        public FinalSawOpcViewModel(UaApplication application) : base(application) { }

        private short _pressure;
        [MonitoredItem(nodeId: FinalSawHelper.Opc.ITEM_PRESSURE)]
        public short Pressure
        {
            get => _pressure;
            set => SetProperty(ref _pressure, value);
        }

        private short _cutSpeed;
        [MonitoredItem(nodeId: FinalSawHelper.Opc.ITEM_CUT_SPEED)]
        public short CutSpeed
        {
            get => _cutSpeed;
            set => SetProperty(ref _cutSpeed, value);
        }

        private short _rollsSpeed;
        [MonitoredItem(nodeId: FinalSawHelper.Opc.ITEM_ROOLS_SPEED)]
        public short RollsSpeed
        {
            get => _rollsSpeed;
            set => SetProperty(ref _rollsSpeed, value);
        }

        private short _beltsSpeed;
        [MonitoredItem(nodeId: FinalSawHelper.Opc.ITEM_BELTS_SPEED)]
        public short BeltsSpeed
        {
            get => _beltsSpeed;
            set => SetProperty(ref _beltsSpeed, value);
        }

        private short _length;
        [MonitoredItem(nodeId: FinalSawHelper.Opc.ITEM_ACTIVE_ORDER_LENGTH)]
        public short Length
        {
            get => _length;
            set => SetProperty(ref _length, value);
        }


        private int _weight;
        [MonitoredItem(nodeId: FinalSawHelper.Opc.ITEM_WEIGHT_SCALE)]
        public int Weight
        {
            get => _weight;
            set => SetProperty(ref _weight, value);
        }

        private short _r1ProfileNr;
        [MonitoredItem(nodeId: FinalSawHelper.Opc.ITEM_R1_PROFILE_NR)]
        public short R1ProfileNr
        {
            get => _r1ProfileNr;
            set => SetProperty(ref _r1ProfileNr, value);
        }

        private short _r3LayerNr;
        [MonitoredItem(nodeId: FinalSawHelper.Opc.ITEM_R3_LAYER_NR)]
        public short R3LayerNr
        {
            get => _r3LayerNr;
            set => SetProperty(ref _r3LayerNr, value);
        }

        private OpcTrackLayer _roll3;
        [MonitoredItem(nodeId: FinalSawHelper.Opc.TRACK_LAYER_ROLL3)]
        public OpcTrackLayer Roll3
        {
            get => _roll3;
            set => SetProperty(ref _roll3, value);
        }
    }

Helper


        private static UaApplication _uaApplication;
        public static UaApplication GetUaApplication()
        {
            if (_uaApplication == null)
            {
                _uaApplication = new UaApplicationBuilder()
                                    .SetApplicationUri($"urn:{Dns.GetHostName()}:EPMS")
                                    .SetIdentity(new AnonymousIdentity())

                                    //.AddMappedEndpoint(DieOvenHelper.Opc.REQUEST_URL,
                                    //                   EPMSSettings.Default.OpcUrlPlcDieOvens,
                                    //                   SecurityPolicyUris.None)

                                    .AddMappedEndpoint(ExtrusionHelper.Press.Opc.REQUEST_URL,
                                                       EPMSSettings.Default.OpcUrlPlcExtrusionPress,
                                                       SecurityPolicyUris.None)

                                    .AddMappedEndpoint(ExtrusionHelper.BilletOven.Opc.REQUEST_URL,
                                                       EPMSSettings.Default.OpcUrlPlcExtrusionSawFurnace,
                                                       SecurityPolicyUris.None)

                                    .AddMappedEndpoint(FinalSawHelper.Opc.REQUEST_URL,
                                                       EPMSSettings.Default.OpcUrlPlcFinalSaw,
                                                       SecurityPolicyUris.None)

                                    .AddMappedEndpoint(OvenHelper.Opc.REQUEST_URL,
                                                       EPMSSettings.Default.OpcUrlPlcOvens,
                                                       SecurityPolicyUris.None)

                                    .AddMappedEndpoint(PackingHelper.Opc.REQUEST_URL,
                                                       EPMSSettings.Default.OpcUrlPlcPacking,
                                                       SecurityPolicyUris.None)

                                    .Build();

                _uaApplication.Run();
            }

            return _uaApplication;
        }

Usage

        public UCFinalSawStation()
        {
            InitializeComponent();

....
            _viewModel = new FinalSawOpcViewModel(OpcHelper.GetUaApplication());
            _viewModel.PropertyChanged += ViewModel_PropertyChanged;
            dlcFinalSaw.DataSource = _viewModel;

....
        }

 private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
 {
            switch (e.PropertyName)
            {
.....
            }
}

Best reagrds

@awcullen
Copy link
Contributor

Thanks for the code. Everything looks okay. Tell us more about when the ViewModel does not write to the PLC. Are you using an editor that is bound to a property of the ViewModel?

The library has trouble with setting properties when:

  • from a non-UI thread
  • in the ViewModel_PropertyChanged handler
  • when the PLC is reconnecting

@franklupo
Copy link
Author

Hi,
has trouble with:

  • from a non-UI thread
  • in the ViewModel_PropertyChanged handler

In some case using editor.

best regards

@awcullen
Copy link
Contributor

From a non-UI thread, or from the PropertyChanged handler you need to do one of the following:

Option 1

        case "Length":
                    Dispatcher.CurrentDispatcher.InvokeAsync(() => { this.Weight = this.Length *42; });

option 2

        case "Length":
                    Task.Run(async () =>
                    {
                        try
                        {
                            await this.InnerChannel.WriteAsync(new WriteRequest
                            {
                                NodesToWrite = new[]
                                {
                                    new WriteValue
                                    {
                                        NodeId = NodeId.Parse(FinalSawHelper.Opc.ITEM_WEIGHT_SCALE),
                                        AttributeId = AttributeIds.Value,
                                        Value = new DataValue((int)(this.Length *42))
                                    }
                                }
                            });
                        }
                        catch (Exception ex)
                        {
                            Debug.WriteLine("Error writing value : {0}", ex.Message);
                        }
                    });
                

@franklupo
Copy link
Author

I create extension with reflection and write/read value and retrive nodeid from attribute.

Best regards

@franklupo
Copy link
Author

hI,
i create extension to get nodeId from property, and write property using expression.

Best regards

public static class OpcExtension
{
	#region Subscription
	public static string GetNodeId<T>(this T subscription, string propertyName) where T : SubscriptionBaseEx
	   => ((MonitoredItemAttribute)subscription.GetType()
											   .GetProperty(propertyName)
											   .GetCustomAttributes(typeof(MonitoredItemAttribute), true)
											   .FirstOrDefault()).NodeId;

	public static string GetNodeId<T>(this T subscription, Expression<Func<T, object>> expression) where T : SubscriptionBaseEx 
		=> subscription.GetNodeId(NameReaderExtensions.GetMemberName(subscription, expression));

	public static WriteResponse WriteNode<T>(this T subscription,
											 Expression<Func<T, object>> expression,
											 object value) where T : SubscriptionBaseEx
	{
		var propertyName = NameReaderExtensions.GetMemberName(subscription, expression);
		var newValue = Convert.ChangeType(value, subscription.GetType().GetProperty(propertyName).PropertyType);
		return subscription.WriteNode(subscription.GetNodeId(propertyName), newValue);
	}
}
 public static class NameReaderExtensions
    {
        public static string GetMemberName<T>(this T instance, Expression<Func<T, object>> expression) where T: INameReaderExtensions
            => GetMemberName(expression.Body);

        public static List<string> GetMemberNames<T>(this T instance, params Expression<Func<T, object>>[] expressions) where T : INameReaderExtensions
            => expressions.Select(a => GetMemberName(a.Body)).ToList();

        public static string GetMemberName<T>(this T instance, Expression<Action<T>> expression) where T : INameReaderExtensions
            => GetMemberName(expression.Body);

        private static string GetMemberName(Expression expression)
        {
            if (expression == null) { throw new ArgumentException("The expression cannot be null."); }

            // Reference type property or field
            if (expression is MemberExpression memberExpression) { return memberExpression.Member.Name; }

            // Reference type method
            if (expression is MethodCallExpression methodCallExpression) { return methodCallExpression.Method.Name; }

            // Property, field of method returning value type
            if (expression is UnaryExpression unaryExpression) { return GetMemberName(unaryExpression); }
            throw new ArgumentException("Invalid expression.");
        }

        private static string GetMemberName(UnaryExpression unaryExpression)
        {
            if (unaryExpression.Operand is MethodCallExpression methodExpression) { return methodExpression.Method.Name; }
            return ((MemberExpression)unaryExpression.Operand).Member.Name;
        }
    }

@awcullen
Copy link
Contributor

Very nice! Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants