function data_scope_show( op, a1 )
%
% Plot signals from PLC assuming:
% - Uploaded "data_scope.stu" to the PLC simulator, and running
% - section "tst_data_log" set to 1 %m92 or %m93
% - run this Matlab program without arguments
% - stop this program by putting the mouse pointer near the "Start" button

% June2025 JG

if nargin<1
    op= 'show';
end

switch op
    case 'show',  tst5_plc_plot(1);
    case 'show0', tst5_plc_plot(0);
    case 'tst', unit_tst(a1);
    otherwise
        error('inv op "%s"', op)
end

return


% ------------------------------------------------------------------------
function unit_tst( tstId )
switch tstId
    case 0, tst0_plc_to_matfile(1);
    case 1, tst1_matfile_printf;
    case 2, tst2_plc_printf;
    case 3, tst3_plc_printf2;
    case 4, tst4_plc_plot;
    case 5, tst5_plc_plot;
end


function tst0_plc_to_matfile( findBuffLengthFlag )

if ~findBuffLengthFlag
    % assume 2x11 buffer
    firstReg= 91;
    nRegs= 31; % specific buff size in the PLC, may be wrong
    regValues= PLC_read_regs( firstReg, nRegs );
else
    % see buffer size in %mw99
    regValues= PLC_read_buffer;
end

% save ex3_get_data_tmp regValues
ofname= mkfname( 'data/ex3_', 'mat', struct('outputFormat',3) );
save(ofname, 'regValues');
return


function tst1_matfile_printf
% load('ex3_get_data_tmp.mat', 'regValues');
ifname= filenames_last_only( 'data/ex3_*.mat' );
load(ifname, 'regValues');
[tu, ~]= regValues2table( regValues );
tu
myplotz(tu)
return


% ------------------------------------------------------------------------
function tst2_plc_printf
% loop by cleaning the PLC buffer memory
PLC_clear_buffer([]); pause(2)
x= {};
for i=1:3
    regValues= PLC_read_buffer;
    x{i,1}=regValues(1:9); x{i,2}= reshape(regValues(10:end),2,[]);
    [tu, ~]= regValues2table( regValues );
    PLC_clear_buffer(regValues);
    disp(tu)
    pause(2)
end
return


function tst3_plc_printf2
% Loop by cleaning the PLC buffer memory
% Do a Matlab buffer to hold data comming from the PLC
% (Make just one 2xN buffer, or make multiple ones?)
% (Make one circular, large/"last minute", 2xN buffer? 1e5 samples?)

cb = CircularBuffer(1000);
PLC_clear_buffer([]); pause(2)

for i=1:3
    regValues= PLC_read_buffer;
    [tu, ~]= regValues2table( regValues );
    PLC_clear_buffer(regValues);
    disp(tu)
    for j= 1:size(tu,1)
        cb.addSample( tu(j,1), tu(j,2) );
    end
    pause(2)
end

disp(cb.getBuffer())
return


function tst4_plc_plot
% Loop by cleaning the PLC buffer memory
% Do a Matlab buffer to hold data comming from the PLC
% (Make just one 2xN buffer, or make multiple ones?)
% (Make one circular, large/"last minute", 2xN buffer? 1e5 samples?)

cb = CircularBuffer(1000);
PLC_clear_buffer([]); pause(2)

for i=1:10
    regValues= PLC_read_buffer;
    [tu, ~]= regValues2table( regValues );
    PLC_clear_buffer(regValues);
    disp(tu)
    for j= 1:size(tu,1)
        cb.addSample( tu(j,1), tu(j,2) );
    end
    pause(2)
end

x= cb.getBuffer()';
if isempty(x)
    warning('** Got NO data. Is the PLC logging data?')
    return
end

[t, u]= mybitget(x);
figure(201); clf;
plot_z(t, u, struct('patch',1,'zoh',1)); drawnow

return


% ------------------------------------------------------------------------
function [t, z]= mybitget(x0)
t= double(x0(:,1));
z= dec2bin(x0(:,2),16)-'0'; z= z(:,end:-1:1);

% x = uint16(x0(:,2));     % Extract uint16 values (column 2)
% % Preallocate binary matrix, Logical array is sufficient
% N = length(x);
% z = zeros(N, 16); %false(N, 16);
% % Extract each bit
% for k = 1:16
%     z(:,17-k) = bitget(x, k);   % MSB on the left
% end


function draw_line( xx, yy, cstr )
wasHold= ishold;
hold on
plot( xx, yy, cstr )
if ~wasHold, hold off; end


function myplotz(x, maxDeltaT)
if nargin<2
    maxDeltaT= Inf;
end
if isempty(x)
    warning('** Got NO data. Is the PLC logging data?')
    return
end

[t, u]= mybitget(x);
t(t<0)= t(t<0)+65536;

ind= find(t(end)-t<maxDeltaT);
t= t(ind);
u= u(ind,:);
if length(t)<2
    warning('** Not enough data for the selected time window')
    return
end

figure(201); clf;
plot_z(t, u, struct('patch',1,'zoh',1));
%draw_line( [t(end)-maxDeltaT t(end)], [0 0], 'k' );
axis tight
title('PLC Scope (stop with mouse ptr near "Start button")')
xlabel('scan cycle')
drawnow


function tst5_plc_plot( cleanStartFlag )
% Loop by cleaning the PLC buffer memory
% Do a Matlab buffer to hold data comming from the PLC
% (Make just one 2xN buffer, or make multiple ones?)
% (Make one circular, large/"last minute", 2xN buffer? 1e5 samples?)

global CB
if isempty(CB) || cleanStartFlag
    CB = CircularBuffer(1000);
end

T1= .1; %.2; %2/5; % update period [seconds]
T2= 200; %500/5; % display window (#scan cycles)
PLC_clear_buffer([]); pause(T1)

aborttst(1);
while 1
%for i=1:10
    regValues= PLC_read_buffer;
    [tu, ~]= regValues2table( regValues );
    %tu= [typecast(tu(:,1), 'uint16') typecast(tu(:,2), 'uint16')];
    PLC_clear_buffer(regValues);
    %disp(tu)
    for j= 1:size(tu,1)
        CB.addSample( tu(j,1), tu(j,2) );
    end
    pause(T1)
    %myplotz( cb.getBuffer()' )
    myplotz( CB.getBuffer()', T2 )
    if aborttst, break; end
end

return


% ------------------------------------------------------------------------
% TODO more on "global CB": "save raw", "save min", "load"
% - allow also in the future to change the buffer size as a parameter
% - allow also save till buffer full

% ------------------------------------------------------------------------
function regValues= PLC_read_buffer
nRegs= PLC_read_regs( 99, 1 );
nRegs= prod([floor(nRegs/1000) rem(nRegs,1000)]) +9;
regValues= PLC_read_regs( 91, nRegs );


function PLC_clear_buffer(regValues)
if isempty(regValues)
    PLC_write_regs( 92, 0 );
    PLC_write_regs( 96, 0 );
else
    PLC_write_regs( 92, regValues(6) );
end


function regValues= PLC_read_regs( firstReg, nRegs )
m= mymodbus2('ini');
regValues= mymodbus2('read', m, 'holdingregs', firstReg, nRegs);
mymodbus2('end', m);


function PLC_write_regs( firstReg, regValues )
m= mymodbus2('ini');
mymodbus2('write', m, 'holdingregs', firstReg, regValues );
mymodbus2('end', m);


% ------------------------------------------------------------------------

function [ret, info]= regValues2table( regValues )
%1        -4096  %mw91 := 16#F000; (* a marker for Matlab to find *)
%2            0  %mw92 := 0;     (* ==> a "consumed" index into the array *)
%3        16286  %mw93 := 0;     (* "scan_cycle_num" a scan cycle counter *)
%4          756  %mw94 := 0;     (* "acc" an accumulator of values *)
%5           25  %mw95 := 0;     (* "last_acc" previous value of mw94 *)
%6            0  %mw96 := 0;     (* ==> an index into the array *)
%7        12345  %MW97 := 12345; (* a marker for Matlab to find *)
%8        12345  %MW98 := 12345; (* a marker for Matlab to find *)
%9         2011  %mw99 := 02011; (* Matlab matrix 02x011, only 02x010 used *)

% copy PLC array (regValues) to a local struct (BD)
s2= get_size( regValues(9) ); %[2 11];
sz= prod(s2);
bb= regValues(10:10+sz-1); bb= bb(:)';
i1= regValues(6);
i2= regValues(2);
BD= struct('s2',s2, 'sz',sz, 'buff',bb, 'i1',i1, 'i2',i2);

% interpret the content of the struct to extract the valid values
ret= get_table(BD);
ret= reshape(ret, s2(1),[] )';

% extra info to return
[flags, isOk]= get_flags( regValues(1) );
info= struct('s2',s2, 'sz',sz, 'flags',flags, 'isOk',isOk);
return


function s2= get_size( regValue9 )
s2= [floor(regValue9/1000) rem(regValue9,1000)];


function [flags, isOk]= get_flags( regValue1 )
flags= typecast(regValue1, 'uint32');
mask = hex2dec('F000');
isOk = ( bitand(flags, mask) == mask );
mask = hex2dec('0FFF');
flags= bitand(flags, mask);


function ret= get_table(BD)

if BD.i2==BD.i1
    % empty buffer
    ret= []; return
end

% data is bounded by i1 and i2 in a circular buffer
% ( s2(1) is the sample size, typically 2 = timesample + value )
% ( +1 is needed to translate array indexes from the PLC to Matlab )
s1= BD.s2(1);
i1= s1*BD.i1 +1;
i2= s1*buff_ind_incr(BD.i2, BD.s2) +1;

if i2>i1
    % 0<= i1 < i2 <= sz-1
    ret= [BD.buff(i2:end) BD.buff(1:i1+(s1-1))];
else
    % 0<= i2 < i1 <= sz-1
    ret= BD.buff( i2:i1+(s1-1) );
end

% % debug (to delete; for now is garanteed the plc fills non zero values)
% if sum(ret<1)>0
%     fprintf(1, '** got %d zeros\n', sum(ret<1));
% end

return


function pi2= buff_ind_incr( pi1, s2 )
% using PLC indexing 0,1,2,... => real address s2(1)*pi1 = 0,2,4,...
pi2= pi1+1; if pi2>=s2(2), pi2= 0; end


function i2= clear_plc_buff(BD)
% circular buffer is empty when first and last indexes are equal
i2= BD.i1;
