Home > Enterprise >  How can I use I timer in my Blazor server app, to perform an action periodically?
How can I use I timer in my Blazor server app, to perform an action periodically?

Time:07-26

Hello I have a Blazor server app where some PLC variables are read from a remote production machine. The code part that is connecting to the PLC and reading the datas is in a sourced service (PLCService.cs). The service is injected and called in a razor page. If I trigger the reading manualy with a button, all the variables are read correctly. So far no problem. But I want that the variables are read every second from the remote machine's PLC. Therefore I have programmed a timer in the codebehind page of my razor page (not in the service), but the timer is not working. (The values are note read even once) In the razor page there are also the variables that I read from the PLC, but for making it leaner I have just shown the counter value, that should count each second.

In my razor file:

@inject PLCService PLCService
<button @onclick="Read_CNC_Status">Read PLC Data</button>
<l>@counter_timer</l>  // Unfortunately the counter value is always "0"

In my razor.cs file:

using System.Timers;
public partial class Read_PLC_Data
{
    public int counter_timer=0; 
    System.Timers.Timer timer1 = new System.Timers.Timer();
    public async void Read_CNC_Status()
    {
        PLCService.Connect_PLC(); // Connection code is in a sourced service
        Initialise_Timer1();
    }

    public void Initialise_Timer1()
    {            
        timer1.Elapsed  = new ElapsedEventHandler(OnTimedEvent1);
        timer1.Interval = 1000;
        timer1.Enabled = true;
        counter_timer = 0;
    }

    public void OnTimedEvent1(object source, ElapsedEventArgs e)
    {
        PLCService.Read_PLC_Data();            
        counter_CNC_status  = 1;  // This counter is not counting !!!
        if(counter_timer >= 30)
        {
            counter_timer = 0;
            timer1.Enabled = false;

        }
        StateHasChanged();
    }

}

CodePudding user response:

I don't see where you are calling the Read_CNC_Status() method. If you don't call it, then nothing you wrote about the timer is ever executed. You can call it in the component's OnAfterRender function (or in the constructor of your partial class).

protected override void OnAfterRender(bool firstRender)
{
    if (firstRender)
    {
        Read_CNC_Status();
    }
    base.OnAfterRender(firstRender);
}

I ran the rest of the code and it works.

EDIT: It's not necessary, but I also recommend that you make sure the handler is not called again before the previous execution is finished (this could happen for example if Read_PLC_Data() is slow and takes more than the timer's Interval to complete). To do that, you can set the AutoReset property of your timer to false and manually restart the timer each time at the end of your handler, like this:

public async void Read_CNC_Status()
{
    PLCService.Connect_PLC(); // Connection code is in a sourced service
    Initialise_Timer1();
}

public void Initialise_Timer1()
{            
    timer1.Elapsed  = new ElapsedEventHandler(OnTimedEvent1);
    timer1.Interval = 1000;
    timer1.AutoReset = false;
    counter_CNC_status = 0;
    timer1.Start();
}

public void OnTimedEvent1(object source, ElapsedEventArgs e)
{
          
    try
    {
        PLCService.Read_PLC_Data();      
        counter_CNC_status  = 1;  // This counter is not counting !!!
    }
    finally
    {
        if(counter_CNC_status >= 30)
        {
            counter_CNC_status = 0;
            timer1.Enabled = false;
        }
        else
        {
            timer1.Start();
        }
    }
    StateHasChanged();
}

CodePudding user response:

Try the periodic timer:

private readonly PeriodicTimer _periodicTimer = new(TimeSpan.FromSeconds(5));

public async void OnGet()
{
    while(await _periodicTimer.WaitForNextTickAsync())
    {
        await ConnectPlc();
    }
}

UPDATE

I made an investigation about the periodic timer in blazor and i didn't figure out how to re-render the page in order to get back the "refreshed" counter. So in order to get the new counter value after the time interval, i ended up with this solution:

<h3>@_currentCount</h3>

@code {
    private int _currentCount;
    private System.Timers.Timer _timer;

   protected override void OnInitialized()
   {
       _timer = new();
       _timer.Interval = 1000;
       _timer.Elapsed  = async (object? sender, ElapsedEventArgs e) =>
       {
           _currentCount  ;               
           await InvokeAsync(StateHasChanged);
       };
       _timer.Enabled = true;
   }
}

Don't forget to invoke the StateHasChanged method

CodePudding user response:

The best approach for me is to use QUARTZ.NET

  • Related