如何在MATLAB中使用计时器以固定间隔运行函数

时间:2019-07-19 22:23:01

标签: matlab timer

我想每隔30分钟运行一个函数。每次运行该函数时,都会采用不同的输入。假设我要运行100次。函数为lookupweather,输入为location1,location2,location3,...,location100

我尝试过:

for a = 1:100
    t = timer;          
    t.StartDelay = 30*60*(a-1)       
    t.TimerFcn = @(scr, event) run('lookupweather');
    start(t)
end 

这里的问题是我找不到地方输入位置信息。如果我尝试了一些lookupweather(location1),则代码将失败。当然,如果没有位置输入,lookupweather函数将失败。有人可以帮忙吗?

编辑:我意识到我可以固定间隔

t = timer;
t.Period = 30*60;
t.TasksToExecute = 100;
t.ExecutionMode = 'fixedRate';
t.TimerFcn = @(src, event) run('lookupweather');
start(t)

但是,我仍然不知道如何将位置信息输入到我的函数lookupweather中。

2 个答案:

答案 0 :(得分:3)

您需要使用单元格数组声明计时器回调函数,如下所示:

location_index = 1;

t = timer;
t.Period = 1;  %30*60;
t.TasksToExecute = 5;  %100;
t.ExecutionMode = 'fixedRate';
t.TimerFcn = {@timer_callback, location_index};
start(t);

process_locations = true;
while process_locations
    % . . . 
end
stop(t);
delete(t);

function timer_callback(obj, event, location_index)
    fprintf("Location index = %03d\n", location_index);
end

您可能还需要使用位置的一维向量(或数组),如下所示:

locations = zeros(1, 100);

t = timer;
t.Period = 1;  %30 * 60;
t.TasksToExecute = 5;  %100;
t.ExecutionMode = 'fixedRate';
%t.TimerFcn = {@timer_callback2};
t.TimerFcn = {@timer_callback3, locations};
start(t);

process_locations = true;
while process_locations
    % . . . 
end
stop(t);
delete(t);

function timer_callback2(obj, event)
    persistent location_index;
    if isempty(location_index)
        location_index = 1;
    end
    fprintf("Location index = %03d\n", location_index);
    location_index = location_index + 1;
end

function timer_callback3(obj, event, locations)
    persistent location_index
    if isempty(location_index)
        location_index = 1;
    end
    locations(location_index) = 12.3;  % Get value from temperature sensor.
    fprintf("locations(%03d) = %f\n", location_index, locations(location_index));
    location_index = location_index + 1;
end

版本4

这使用在计时器回调中修改的全局结构。考虑将其封装在处理程序类或嵌套函数中,以避免使用全局变量。

clear all;
clc;

number_of_iterations = 10;  % 100
number_of_locations = 5;

% Create a global struct for the data.
% Consider encapsulating in a class rather than using a global.
global temperature_data;
temperature_data = struct("IterationIndex", 1, "Processed", false, "Locations", zeros(number_of_iterations, number_of_locations));

t = timer;
t.Period = 1;  %30 * 60;
t.TasksToExecute = number_of_iterations;
t.ExecutionMode = 'fixedRate';
t.TimerFcn = {@TimerCallback4};
start(t);
while temperature_data.Processed == false
        % . . .
        % Yield some processing time.
        time_delay = t.Period * 1000 / 10;
        java.lang.Thread.sleep(time_delay);
end
stop(t);
delete(t);

function TimerCallback4(obj, event)
    global temperature_data;

    % Cycle through locations.
    for location_index = 1:5
        % Get value from temperature sensor.
        temperature_data.Locations(temperature_data.IterationIndex, location_index) = 100 * rand;
        fprintf("temperature_data(%03d, %d) = %5.2f\n", temperature_data.IterationIndex, location_index, temperature_data.Locations(temperature_data.IterationIndex, location_index));
    end

    % Test for completion of processing.
    if temperature_data.IterationIndex >= size(temperature_data.Locations, 1)
        temperature_data.Processed = true;
    else
        temperature_data.IterationIndex = temperature_data.IterationIndex + 1;
    end
end

版本4结果

TimerCallback4() 0.058
TimerCallback4() 1.023
TimerCallback4() 2.033
TimerCallback4() 3.042
TimerCallback4() 3.961
TimerCallback4() 4.975
TimerCallback4() 5.982
TimerCallback4() 6.990
TimerCallback4() 8.002
TimerCallback4() 9.008
   10.7889   18.2228    9.9095   48.9764   19.3245
   89.5892    9.9090    4.4166   55.7295   77.2495
   31.1940   17.8982   33.8956   21.0146   51.0153
   90.6364   62.8924   10.1534   39.0855    5.4617
   50.1283   43.1721   99.7560   81.1603   48.5652
   89.4448   13.7547   39.0005   92.7356   91.7494
   71.3574   61.8337   34.3288   93.6027   12.4774
   73.0585   64.6477   83.3152   39.8282   74.9822
   83.5221   32.2460   55.2262   97.9129   54.9309
   33.0424   61.9472   36.0637   75.6510   41.3901

版本5

此版本使用句柄类。它可以同步或异步处理。

Test.m

    clear all;
    clc;

    % Define the settings.
    number_of_iterations = 10;  % 100
    number_of_locations = 5;
    period = 1;  % 30 * 60  % Seconds.

    % Create the object with required settings.
    temperature_processor = TemperatureProcessor(number_of_iterations, number_of_locations, period);

    % Do the process synchronously.
    temperature_processor.ProcessSync();
    disp(temperature_processor.Locations);

    % Do the process asynchronously.
    temperature_processor.IsProcessed = false;
    temperature_processor.ProcessAsync();
    while temperature_processor.IsProcessed == false
        % Do other stuff.
        % . . .

        % Yield some processing time.
        %pause(0.001);
        java.lang.Thread.sleep(1);  % milliseconds.
    end
    disp(temperature_processor.Locations);

    % Delete the object.
    delete(temperature_processor);

TemperatureProcessor.m

    classdef TemperatureProcessor < handle

        properties
            IsProcessed = false;
            Locations;
        end

        properties (Access = private)
            % Define default values.
            NumberOfIterations = 100;
            NumberOfLocations = 5;
            Period = 30 * 60;  % Seconds.
            AsyncIterationIndex = 1;
            AsyncTimer;
        end

        methods
            % Constructor.
            function obj = TemperatureProcessor(number_of_iterations, number_of_locations, period)
                fprintf("obj.TemperatureProcessor() constructor\n");

                if nargin == 3
                    obj.NumberOfIterations = number_of_iterations;
                    obj.NumberOfLocations = number_of_locations;
                    obj.Period = period;
                end
                obj.Locations = zeros(obj.NumberOfIterations, obj.NumberOfLocations);
            end

            % Destructor.
            function delete(obj)
                fprintf("obj.delete() destructor\n");
                try
                    stop(obj.AsyncTimer);
                    delete(obj.AsyncTimer);
                catch
                end
            end

            function ProcessSync(obj)
                fprintf("obj.ProcessSync()\n");

                iteration_index = 1;
                the_timer = timer;
                the_timer.Period = obj.Period;
                the_timer.TasksToExecute = obj.NumberOfIterations;
                the_timer.ExecutionMode = 'fixedRate';
                the_timer.TimerFcn = {@TimerCallbackSync};
                tic;
                start(the_timer);
                wait(the_timer);
                delete(the_timer);

                function TimerCallbackSync(timer_obj, timer_event)
                    fprintf("obj.Process.TimerCallbackSync() %0.3f\n", toc);

                    % Cycle through locations.
                    for location_index = 1:obj.NumberOfLocations
                        % Get value from temperature sensor.
                        obj.Locations(iteration_index, location_index) = 100 * rand;
                        fprintf("obj.Locations(%03d, %d) = %5.2f\n", iteration_index, location_index, obj.Locations(iteration_index, location_index));
                    end

                    % Test for completion of processing.
                    if iteration_index >= obj.NumberOfIterations
                        obj.IsProcessed = true;
                    else
                        iteration_index = iteration_index + 1;
                    end
                end
            end

            function ProcessAsync(obj)
                fprintf("obj.ProcessAsync()\n");

                try
                    stop(obj.AsyncTimer);
                    delete(obj.AsyncTimer);
                catch
                end
                obj.AsyncIterationIndex = 1;
                obj.AsyncTimer = timer;
                obj.AsyncTimer.Period = obj.Period;
                obj.AsyncTimer.TasksToExecute = obj.NumberOfIterations;
                obj.AsyncTimer.ExecutionMode = 'fixedRate';
                obj.AsyncTimer.TimerFcn = {@obj.TimerCallbackAsync};
                tic;
                start(obj.AsyncTimer);
            end

            function TimerCallbackAsync(obj, timer_obj, timer_event)
                fprintf("obj.Process.TimerCallbackAsync() %0.3f\n", toc);

                % Cycle through locations.
                for location_index = 1:obj.NumberOfLocations
                    % Get value from temperature sensor.
                    obj.Locations(obj.AsyncIterationIndex, location_index) = 100 * rand;
                    fprintf("obj.Locations(%03d, %d) = %5.2f\n", obj.AsyncIterationIndex, location_index, obj.Locations(obj.AsyncIterationIndex, location_index));
                end

                % Test for completion of processing.
                if obj.AsyncIterationIndex >= obj.NumberOfIterations
                    try
                        stop(obj.AsyncTimer);
                        delete(obj.AsyncTimer);
                    catch
                    end
                    obj.IsProcessed = true;
                else
                    obj.AsyncIterationIndex = obj.AsyncIterationIndex + 1;
                end
            end
        end
    end

版本5结果

obj.TemperatureProcessor() constructor
obj.ProcessSync()
obj.Process.TimerCallbackSync() 0.051
obj.Process.TimerCallbackSync() 1.029
obj.Process.TimerCallbackSync() 2.026
obj.Process.TimerCallbackSync() 3.025
obj.Process.TimerCallbackSync() 4.034
obj.Process.TimerCallbackSync() 5.024
obj.Process.TimerCallbackSync() 6.023
obj.Process.TimerCallbackSync() 7.023
obj.Process.TimerCallbackSync() 8.023
obj.Process.TimerCallbackSync() 9.023
obj.ProcessAsync()
obj.Process.TimerCallbackAsync() 0.009
obj.Process.TimerCallbackAsync() 1.005
obj.Process.TimerCallbackAsync() 2.004
obj.Process.TimerCallbackAsync() 3.005
obj.Process.TimerCallbackAsync() 4.007
obj.Process.TimerCallbackAsync() 5.005
obj.Process.TimerCallbackAsync() 6.005
obj.Process.TimerCallbackAsync() 7.005
obj.Process.TimerCallbackAsync() 8.005
obj.Process.TimerCallbackAsync() 9.005
obj.delete() destructor

答案 1 :(得分:1)

对于一般解决方案,您可以创建自己的Event and Listener类。

例如,创建一个名为sampleEvent.m的新.m文件,并在其中写入

classdef weather < handle
    events
        lookup_timedriven
    end
    methods
        function lookup(self)
             notify(self,'lookup_timedriven')
        end
        %%%% optional
        function self = weather(self)
            t = timer;
            t.StartDelay = 1; % leave some time for event handler to be created
            t.TimerFcn = @()[];  % You can't create a true empty function_handle unless you have a do nothing function in your library.
            start(t)
            for a = 0:99 % the weather calls
              t = timer;          
              t.StartDelay = 30*60*a;
              t.TimerFcn = @self.lookup;
              start(t)
            end 
        end
        %%%%
    end
end

和另一个名为sampleListener.m的.m文件,您可以在其中写入

classdef sampleListener < handle
   methods
      function sampleListener(weather_obj)
         lh = addlistener(weather_obj,'lookup_timedriven',@sampleListener.handleEvnt);
      end
   end
   methods (Static) % static is a must. Because addlistener calls the function without instantiating an object
      function handleEvnt(src,~)
         cellfun(@lookupweather, cellOfLocations, 'UniformOutput', false);
      end
   end
end

假设您的函数lookupweather接受1个参数并将数据正确存储在幕后,并且cellOfLocations是“位置”的单元格数组。每当触发事件时,您都可以将cellfun(@lookupweather, cellOfLocations, 'UniformOutput', false)替换为想要执行的操作。 Matlab允许您使用event-specific data

如果您不熟悉Maltab中的对象,请参见this mathwork article。基本上,在开始任何计时例程之前,您需要使用

实例化事件和处理程序类。
weatherEvent = weather;
weatherEventHandler = sampleListener(weather);

现在,您只需每30分钟或以您希望的其他方式调用weather.lookup即可触发事件。

实际上,如果希望在后台运行timer,则可以使用weatherlookup。您已经知道timer的工作方式,在我上面的示例中,weather.lookup没有任何参数。因此,您遇到的问题不会在这里发生。

您可以在单独的.m文件中或直接在wheather类中实现计时代码。如果希望计时器自动启动,则可以在weather的构造函数中定义方法,这就是我在示例中显示的内容。


一些评论:

您为weatherlookup指定100个变量没有任何意义。如果是由于Matlab固有地获取了未指定数量的“位置”,则只需一次调用1个“位置”来调用weatherlookup,然后使用cellfun

我也不确定您的timer问题是否是Matlab的真正限制。 .TimerFcn字段can be any function handle。换句话说,您应该尝试将我的示例中的@self.lookup替换为@()notify(self,'lookup_timedriven')

此外,使用Google AppScript + GoogleSheet可能可以更好地实现您的目标。 Google提供了时间驱动的触发器,无需任何额外的操作,并且可以在云端运行您的程序(只要您不经常执行该程序;一次30分钟就可以了)。