How to run a cyclic connection on a worker to update GUI values using the parallel computing toolbox?

2 次查看(过去 30 天)
Dear Matlab Community,
I'm currently working on a Matlab App GUI to monitor variables on a Beckhoff/TwinCAT PLC. While this code snippet is working right now, I want to improve the responsiveness by using the Parallel Computing Toolbox.
All code examples I found of the Toolbox use a function that has a finite runtime. The Beckhoff API uses a "Notification Buffer" (Ringbuffer) which runs basically infinite in the background and gets read by a periodic timer.
Is there a solution in the toolbox to run the buffer (for an infinite time) on a worker and read the buffer values periodically from the object? I would be very grateful if you could give me a bit of advice :) I tried using parfeval and spmd, but my Matlab skills are not that good right now to find a good solution.
function sync(obj, data, sampleTime)
% sync sync variables to with PLC.
% sync(["variableName", "dataType"; ...], sampleTime) sync variable with plc ex sync(["temperature", "LREAL"], 0.5).
%
% sync(["variableName", "dataType"; ...], sampleTime, CycleTime) sync variable with plc ex sync(["temperature", "LREAL"], 0.5, 4000).
%
% See also disconnect, isAlive.
if obj.plc.connected == true
% Check if Cycle time is given, else use PLC Cylce Time
if ~exist("CycleTime", "var")
obj.plcCycleTime = obj.plc.plcCycleTime;
obj.delayTime = obj.plc.plcDelayTime;
else
obj.plcCycleTime = CycleTime;
obj.delayTime = CycleTime*10;
end
% Save sample Time to Object
obj.sampleTime = sampleTime;
% Save Types to Object, all upper case letters
obj.syncTypes = upper(data(:,2));
% Get rows of inputdata
[rows, ~] = size(data);
try
% For each row check dataType then get Handle from PLC
for i = 1 : rows
if ~(contains(['LREAL', 'BOOL', 'STRING', 'UDINT', 'WORD', 'BYTE', 'INT', 'SINT', 'UINT', 'DINT', 'DWORD'], obj.syncTypes(i)))
if contains(['STRUCT', 'ARRAY'], obj.syncTypes(i))
error(strcat("Wrong use of type %s. The functions input should be the %s itself.", obj.syncTypes(i)));
else
error(strcat("The data type %s is not yet supported by this script.", obj.syncTypes(i)));
end
end
% Get Handle
obj.SyncHandles{i} = obj.plc.plcPort.GetVarHandle(data(i, 1));
end
% Create NotifiactionBuffer for each Handle of Cell
% Array
buffer = cellfun(@() CreateNotificationBuffer([], obj.plcCycleTime, obj.delayTime, 1, true), obj.SyncHandles);
for i = 1 : rows
% Save to object
obj.notebuffer{i} = buffer(i);
end
% Start Notofication for each Buffer
cellfun(@(x) StartNotifications(x), obj.notebuffer)
% Initialize value container
obj.value = zeros([rows 1]);
% Set flag
obj.noti = true;
% Create Timer to update Opject value with sample Time
obj.readTimer = timer('TimerFcn', @(~,~)readValues(obj, obj.syncTypes), 'Period', obj.sampleTime, 'ExecutionMode', 'fixedSpacing', 'BusyMode', 'drop');
start(obj.readTimer);
catch e
error(strcat("Error while creating Notification Buffer. ", e.message))
end
else
fprintf('PLC not connected, try to reconnect first.')
end
end
function readValues(obj, types)
[~, v] = cellfun(@(x) ReadBufferEx(x), obj.notebuffer);
for i = 1 : length(types)
buffer{i} = interpret(v{1, i}, types(i));
end
obj.value = buffer;
end

回答(1 个)

Kothuri
Kothuri 2024-5-24
Hi,
For enhancing the responsiveness of the MATLAB App GUI that monitors variables on a Beckhoff/TwinCAT PLC, when dealing with an infinite runtime scenario like reading from a Notification Buffer, leveraging the Parallel Computing Toolbox is a good approach.
By using parfeval, the worker runs indefinitely and reads from the buffer and sends the updates back to the main MATLAB session.
For the communication between the background worker and the main session, you can use parallel.pool.DataQueue,with this the worker can enqueue data that the main session consumes asynchronously.
function sync(obj, data, sampleTime)
% Check PLC connection
if obj.plc.connected ~= true
fprintf('PLC not connected, try to reconnect first.\n');
return;
end
% Initialization steps as in your original code...
% (Set up obj.plcCycleTime, obj.sampleTime, obj.syncTypes, etc.)
try
% Create Notification Buffers and Start Notifications as before
% ...
% Create a DataQueue for inter-thread communication
obj.dataQueue = parallel.pool.DataQueue;
% Define the DataQueue's afterEach function to process incoming data
afterEach(obj.dataQueue, @(data) processBufferedData(obj, data));
% Start the background worker for continuous monitoring
obj.bufferWorker = parfeval(@continuousMonitoring, 0, obj, sampleTime);
catch e
error(strcat("Error while setting up continuous monitoring. ", e.message));
end
end
function continuousMonitoring(obj, sampleTime)
% Infinite loop for monitoring
while true
% Simulate reading from the Notification Buffer
% Replace this with actual buffer reading logic
bufferData = readBufferSimulator(obj); % Placeholder function
% Send the data to the main session via DataQueue
send(obj.dataQueue, bufferData);
% Wait for the next sample time
pause(sampleTime);
end
end
function processBufferedData(obj, bufferData)
% Process the data received from the background worker
% This could involve updating the GUI with the new data
updateGUIWithData(obj, bufferData);% Placeholder function
end
You can try the above code to improve the responsiveness by offloading the continuous monitoring of the PLC's Notification Buffer to a background worker.
You can refer the below link for more information on parfeval

类别

Help CenterFile Exchange 中查找有关 Graphics Performance 的更多信息

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by