diff --git a/.github/workflows/matlab.yml b/.github/workflows/matlab.yml new file mode 100644 index 000000000..44a8dae4f --- /dev/null +++ b/.github/workflows/matlab.yml @@ -0,0 +1,62 @@ +name: Test MATLAB Bindings + +on: [push] + +jobs: + build: + name: Test MATLAB Bindings + runs-on: ubuntu-latest + steps: + - name: Set up Python 3.10 + uses: actions/setup-python@v2 + with: + python-version: '3.10' + - name: Set up MATLAB + uses: matlab-actions/setup-matlab@v2 + with: + release: R2023b + - name: Check out repository + uses: actions/checkout@v3 + with: + submodules: recursive + - name: Install dependencies + run: | + sudo apt-get -qq update + sudo apt-get install -y git cmake graphviz libavahi-common-dev libavahi-client-dev libaio-dev libusb-1.0-0-dev libxml2-dev rpm tar bzip2 gzip flex bison git libzstd-dev + cmake . -DHAVE_DNS_SD=OFF -DPYTHON_BINDINGS=ON + make + sudo make install + sudo make clean + mkdir edeps + cd edeps + git clone https://github.com/analogdevicesinc/libtinyiiod.git + cd libtinyiiod + mkdir build && cd build + cmake -DBUILD_EXAMPLES=OFF .. + make + sudo make install + sudo ldconfig + cd ../.. + git clone -b v0.1.0 https://github.com/analogdevicesinc/iio-emu.git + cd iio-emu + mkdir build && cd build + cmake .. + make + sudo make install + sudo ldconfig + cd ../.. + cd .. + + - name: Install pytest-libiio interface + run: | + pip install pytest pyyaml lxml click + git clone https://github.com/tfcollins/pytest-libiio.git + mv pytest-libiio/pytest_libiio bindings/matlab/ + cd bindings/matlab + python -c 'import pytest_libiio.plugin' + cd ../.. + + - name: Run Tests + uses: matlab-actions/run-command@v2 + with: + command: cd('bindings/matlab');addpath(genpath('.'));runTests;exit() diff --git a/bindings/matlab/common.m b/bindings/matlab/common.m new file mode 100644 index 000000000..76f87a737 --- /dev/null +++ b/bindings/matlab/common.m @@ -0,0 +1,83 @@ +classdef common < handle + % Common class implementation for the libiio library + % + % This class is a common parent class for all libiio classes. It + % provides a common constructor and destructor for all classes. + % + % See also: Context, Device, Channel, Attribute, Buffer, BufferData + % + % ---------------------------------------------------------------------------- + + properties (SetAccess = protected) + % Pointer to the C++ object + pImpl + end + + methods (Access = protected) + function obj = common() + % Constructor + % + % This function creates a new instance of the common class. + % + % See also: delete + % + % ---------------------------------------------------------------------------- + obj.pImpl = []; + end + + function delete(obj) + % Destructor + % + % This function deletes the instance of the common class. + % + % See also: common + % + % ---------------------------------------------------------------------------- + if ~isempty(obj.pImpl) + delete(obj.pImpl); + end + end + + end + + methods (Access = protected, Static) + + function libname = getLibraryName(~) + % Get the library name + % + % This function returns the name of the libiio library. + % + % ---------------------------------------------------------------------------- + libname = 'libiio'; + end + + function checkLibraryLoaded(~) + if ~libisloaded('libiio') + context.loadLibrary() +% error('libiio library not loaded'); + end + end + + function loadLibrary(~) + % Load the libiio library + % + % This function loads the libiio library. + % + libName = 'libiio'; + libiiowrapperh = 'iio-wrapper.h'; + iioh = 'iio.h'; + fp = fileparts(which(iioh)); + loadlibraryArgs = {libiiowrapperh,'includepath',fp,... + 'addheader',iioh}; + if ~libisloaded(libName) + msgID = 'MATLAB:loadlibrary:StructTypeExists'; + warnStruct = warning('off',msgID); + [~, ~] = loadlibrary(libName, loadlibraryArgs{:}); + warning(warnStruct); + end + end + + + end + +end \ No newline at end of file diff --git a/bindings/matlab/context.m b/bindings/matlab/context.m new file mode 100644 index 000000000..427a340b3 --- /dev/null +++ b/bindings/matlab/context.m @@ -0,0 +1,25 @@ +classdef context < common + % Context class implementation for the libiio library + + + methods (Static) + + function ctx = iio_create_context(params, uri) + % Create a new context + % + % This function creates a new context. + context.checkLibraryLoaded(); + if isempty(params) + params = libpointer; + end + ctx = calllib(context.getLibraryName(), 'iio_create_context', params, uri); + end + + function name = iio_context_get_name(ctx) + context.checkLibraryLoaded(); + name = calllib(context.getLibraryName(), 'iio_context_get_name', ctx); + end + + end + +end \ No newline at end of file diff --git a/bindings/matlab/iio-wrapper.h b/bindings/matlab/iio-wrapper.h new file mode 100644 index 000000000..6f2b413c3 --- /dev/null +++ b/bindings/matlab/iio-wrapper.h @@ -0,0 +1,4 @@ +#ifndef MATLAB_LOADLIBRARY +#define MATLAB_LOADLIBRARY +#include +#endif \ No newline at end of file diff --git a/bindings/matlab/test/TestContext.m b/bindings/matlab/test/TestContext.m new file mode 100644 index 000000000..38641595b --- /dev/null +++ b/bindings/matlab/test/TestContext.m @@ -0,0 +1,50 @@ +classdef TestContext < matlab.unittest.TestCase + + properties + uri = 'ip:pluto.local'; + end + + methods(TestClassSetup) + % Start emulation context + function startEmulation(testCase) + %% Start the emulation context + % checkIIOEMU; + [filepath,~,~] = fileparts(mfilename('fullpath')); + folder = strsplit(filepath, filesep); + folder = fullfile(folder{end}); + % Load python virtual environment + pe = getenv('PYTHON_EXE'); + if ~isempty(pe) + p = pyenv(ExecutionMode="OutOfProcess",Version=pe); + else + p = pyenv(ExecutionMode="OutOfProcess"); + end + disp(p); + % Start background process + xmlfile = fullfile(folder, 'pluto.xml'); + ls . + ls pytest_libiio + emu = py.pytest_libiio.plugin.iio_emu_manager(xmlfile); + emu.start(); + % Wait for the emulation to start + pause(5); + end + end + + methods(TestMethodSetup) + % Setup for each test + end + + methods(Test) + % Test methods + + function testCreateContext(testCase) + ctx = context.iio_create_context([],testCase.uri); + name = context.iio_context_get_name(ctx); + disp(name); + assert(ctx); + end + + end + +end \ No newline at end of file diff --git a/bindings/matlab/test/checkIIOEMU.m b/bindings/matlab/test/checkIIOEMU.m new file mode 100644 index 000000000..ee588832e --- /dev/null +++ b/bindings/matlab/test/checkIIOEMU.m @@ -0,0 +1,4 @@ +% Verify iio-emu executable is in the path +[status, r] = system('iio-emu -l'); +% Check error code +assert(~status, 'iio-emu executable not found in the path'); \ No newline at end of file diff --git a/bindings/matlab/test/data.bin b/bindings/matlab/test/data.bin new file mode 100644 index 000000000..e69de29bb diff --git a/bindings/matlab/test/pluto.xml b/bindings/matlab/test/pluto.xml new file mode 100644 index 000000000..49c5cfabe --- /dev/null +++ b/bindings/matlab/test/pluto.xml @@ -0,0 +1,4 @@ +]> diff --git a/bindings/matlab/test/runTests.m b/bindings/matlab/test/runTests.m new file mode 100644 index 000000000..8155e033e --- /dev/null +++ b/bindings/matlab/test/runTests.m @@ -0,0 +1,60 @@ +function runTests() + +import matlab.unittest.TestRunner; +import matlab.unittest.TestSuite; +import matlab.unittest.plugins.TestReportPlugin; +import matlab.unittest.plugins.XMLPlugin +import matlab.unittest.plugins.ToUniqueFile; +import matlab.unittest.plugins.TAPPlugin; +import matlab.unittest.plugins.DiagnosticsValidationPlugin +import matlab.unittest.parameters.Parameter + +runParallel = false; + +suite = testsuite({'TestContext'}); + +try + + runner = matlab.unittest.TestRunner.withTextOutput('OutputDetail',4); + runner.addPlugin(DiagnosticsValidationPlugin) + + xmlFile = 'BindingsTests.xml'; + plugin = XMLPlugin.producingJUnitFormat(xmlFile); + runner.addPlugin(plugin); + + if runParallel + try %#ok + parpool(2); + results = runInParallel(runner,suite); + catch ME + disp(ME); + results = runner.run(suite); + end + else + results = runner.run(suite); + end + + t = table(results); + disp(t); + disp(repmat('#',1,80)); + for test = results + if test.Failed + disp(test.Name); + end + end +catch e + disp(getReport(e,'extended')); + bdclose('all'); + exit(1); +end + +try + poolobj = gcp('nocreate'); + delete(poolobj); +catch ME + disp(ME) +end + +save(['BSPTest_',datestr(now,'dd_mm_yyyy-HH:MM:SS'),'.mat'],'t'); +bdclose('all'); +exit(any([results.Failed])); \ No newline at end of file