%% JY5325 多通道连续环绕 AO 输出（参考 Winform AO Continuous Wrapping Multichannel）
% - 先可视化波形
% - AOMode = ContinuousWrapping
% - Figure 按 Esc 可中断输出
% - 写入 double[,]，形状：样本数 × 通道数（列为通道）
clc;clear;
% =========================
% 常量配置（按需修改）
% =========================
slotNumber       = int32(0);          % 槽位索引
channels         = [0 1 2 3];         % 选择输出通道（示例 4 通道）
updateRate       = 300000.0;           % 采样率(Hz)
samplesToBuffer  = int32(300000);      % 环绕缓冲区样本数

% 每通道波形参数（可独立配置）
waveTypes        = {'SineWave','SquareWave','UniformWhiteNoise','SineWave'};
amplitudes       = [2.0, 1.5, 1.0, 0.8];
frequencies      = [1000, 500, 0, 2000];  % 噪声通道频率无效可为 0

% JY5325 .NET 程序集路径（按实际安装位置修改）
jyDllPath = 'C:\SeeSharp\JYTEK\Hardware\DAQ\JY5325\Bin\JY5325.dll';

% =========================
% 生成并预览波形（建立任务前）
% =========================
N = double(samplesToBuffer);
t = (0:N-1) / updateRate;
% 生成各通道数据
chData = cell(1, numel(channels));
for idx = 1:numel(channels)
    wt  = lower(waveTypes{idx});
    amp = amplitudes(idx);
    frq = frequencies(idx);
    switch wt
        case 'sinewave'
            chData{idx} = amp * sin(2*pi*frq*t);
        case 'squarewave'
            chData{idx} = amp * sign(sin(2*pi*frq*t)); % 50% 占空
        case 'uniformwhitenoise'
            chData{idx} = amp * (2*rand(1, N) - 1);    % 统一白噪声
        otherwise
            error('Unsupported wave type: %s', waveTypes{idx});
    end
end
% 预览
fig = figure('Name','AO Continuous Wrapping (Preview)', ...
             'NumberTitle','off', ...
             'KeyPressFcn', @(src,evt)setappdata(src,'stopRequested',strcmp(evt.Key,'escape')));
setappdata(fig,'stopRequested',false);
rows = numel(channels);
for idx = 1:rows
    subplot(rows,1,idx);
    plot(t, chData{idx}); grid on;
    title(sprintf('Channel %d (%s)', channels(idx), waveTypes{idx}));
    xlabel('Time (s)'); ylabel('Voltage (V)');
end
drawnow;

% =========================
% 载入 .NET 程序集
% =========================
try
    asms = System.AppDomain.CurrentDomain.GetAssemblies();
    found = false;
    for k = 1:numel(asms)
        if contains(char(asms(k).FullName), 'JY5325')
            found = true; break;
        end
    end
    if ~found
        NET.addAssembly(jyDllPath);
    end
catch
    NET.addAssembly(jyDllPath);
end
import JY5325.*

% =========================
% 组装为 double[,]（Samples x Channels）
% =========================
% data2D = NET.createArray('System.Double', N, numel(channels));
% for k = 1:N
%     for c = 1:numel(channels)
%         data2D(k,c) = chData{c}(k);
%     end
% end

% 一次转换，提高性能
% M: N x C 的 MATLAB double 矩阵（行=样本，列=通道）
% M = vertcat(chData{:}).';
% [N, C] = size(M);
% % 1) 目标 .NET 二维数组
% data2D = NET.createArray('System.Double', int32(N), int32(C));
% % 2) 生成按 .NET 行主序展平的一维 double 向量
% flat = reshape(M.', 1, []);          % 1 x (N*C)
% % 3) 一次性字节拷贝到 data2D
% nBytes = int32(numel(flat) * 8);     % double = 8 字节
% System.Buffer.BlockCopy(flat, 0, data2D, 0, nBytes);

% M: N x C 的 MATLAB double 矩阵（行=样本，列=通道）
M=vertcat(chData{:}).';
data2D=M;     % matlab在调用时自动完成转换
% data2D=NET.convertArray(M,'System.Double');     % 可以不用显示转换


% =========================
% 建立并配置 AO 连续环绕任务
% =========================
aoTask = JY5325AOTask(slotNumber);
cleanupObj = onCleanup(@()safeCleanup(aoTask)); % 自动清理
aoTask.SampleClock.Source = AOSampleClockSource.Internal;
for c = 1:numel(channels)
    aoTask.AddChannel(int32(channels(c)));
end
aoTask.Mode       = AOMode.ContinuousWrapping;
aoTask.UpdateRate = updateRate;
% 写入初始环绕缓冲区并启动
aoTask.WriteData(data2D, int32(-1));
aoTask.Start();

% =========================
% 环绕运行与中断控制
% =========================
% 连续环绕模式下，设备循环播放缓冲区数据。
% 如需动态更新缓冲区，可在循环中再次调用 WriteData 续写新段。
while true
    esc = ishandle(fig) && isappdata(fig,'stopRequested') && getappdata(fig,'stopRequested');
    if esc
        break; % 用户按 Esc 中断
    end
    pause(0.1);
end
% 停止与清理
aoTask.Stop();
aoTask.Channels.Clear();