libplctag.NET icon indicating copy to clipboard operation
libplctag.NET copied to clipboard

Example for NotifyPropertyChanged

Open jkoplo opened this issue 2 years ago • 1 comments

Two common use-cases for this library are:

  1. Displaying the value of a Tag on a User Interface.
  2. Keeping a recording the changes to a Tag value over time.

Both of these require monitoring of the tag value, and notification when it changes. Implementing INotifyPropertyChanged is the canonical way of doing this for many .NET UI frameworks. Provide an example that shows how to do this.

jkoplo avatar Oct 08 '22 22:10 jkoplo

I think its going to look something like this in the end, where it extends Tag<M,T> by adding a ValueChanged event.

class NotifyValueChangedTag<M,T> : Tag<M,T>
        where M : IPlcMapper<T>, new()
{
    public event EventHandler<EventArgs> ValueChanged;

    private int previousHash;

    public NotifyValueChangedTag()
    {
        ReadCompleted += NotifyValueChangedTag_ReadCompleted;
    }

    private void NotifyValueChangedTag_ReadCompleted(object sender, TagEventArgs e)
    {
        var currentHash = this.Value.GetHashCode();
        if (currentHash != previousHash)
        {
            ValueChanged?.Invoke(this, EventArgs.Empty);
        }
        previousHash = currentHash;
    }
}

It requires that the type T has a GetHashCode method that is good at detecting changes. For atomic types, the builtin one should be okay, but for Udts they will need to override it.

class MyUdt
{
    public int MyDint { get; set; }
    public float MyFloat { get; set; }

    public override int GetHashCode()
    {
        // Use the tuple shortcut to get an okay hash
        return (MyDint,MyFloat).GetHashCode();
    }

}

class MyUdtMapper : PlcMapperBase<MyUdt>
{
    public override int? ElementSize => 8;

    public override MyUdt Decode(Tag tag, int offset)
    {
        return new MyUdt()
        {
            MyDint = tag.GetInt32(offset),
            MyFloat = tag.GetFloat32(offset + 4)
        };
    }

    public override void Encode(Tag tag, int offset, MyUdt value)
    {
        tag.SetInt32(offset, value.MyDint);
        tag.SetFloat32(offset + 4, value.MyFloat);
    }
}

and should be able to used like this:

var myTag = new NotifyValueChangedTag<DintPlcMapper, int>()
{
    Name = "MyTag",
    Gateway = "127.0.0.1",
    Path = "1,0",
    PlcType = PlcType.ControlLogix,
    Protocol = Protocol.ab_eip,
    AutoSyncReadInterval = TimeSpan.FromMilliseconds(500)
};

myTag.ValueChanged += (s, e) => Console.WriteLine($"{myTag.Name} changed at {DateTime.Now} to {myTag.Value}");


var myUdtTag = new NotifyValueChangedTag<MyUdtMapper, MyUdt>()
{
    Name = "MyUdtTag",
    Gateway = "127.0.0.1",
    Path = "1,0",
    PlcType = PlcType.ControlLogix,
    Protocol = Protocol.ab_eip,
    AutoSyncReadInterval = TimeSpan.FromMilliseconds(500)
};

myUdtTag.ValueChanged += (s, e) => Console.WriteLine($"{myUdtTag.Name} changed at {DateTime.Now} to {myUdtTag.Value.MyDint}|{myUdtTag.Value.MyFloat}");

timyhac avatar Oct 18 '22 11:10 timyhac

ExampleNotifyChanged has been added to the examples list.

timyhac avatar Nov 20 '22 10:11 timyhac