Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sphinx doc #23

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/workflows/doc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Documentation

on: [push, pull_request]

jobs:
BuildDocs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up Python 3.7
uses: actions/setup-python@v2
with:
python-version: 3.7

- name: Install dependencies
run: |
sudo apt install -y python3-numpy
pip install -r CI/doc/requirements_doc.txt
- name: Check doc build
run: |
make -C CI/doc gen_autodocs html

- name: Publish master doc
if: github.ref == 'refs/heads/master'
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./CI/doc/build/html
destination_dir: master
7 changes: 7 additions & 0 deletions CI/ci_flags.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# CI Signaling

Its possible to configure CI on build a project based on flags in the commit message. This is done by adding a special line to the commit that starts with CI: (must be all caps). The line can contain a list of flags separated by semi-colons. The following flags are supported:
- skip=<Name of stage>
- skip_branch=<Name of parallel branch to skip>:<Name of parent stage>
- enable_only_branch=<Name of parallel branch to enable>:<Name of parent stage>
- env:<Name of environment variable>=<Value of environment variable>
26 changes: 26 additions & 0 deletions CI/doc/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build

PYTHON = python

# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

gen_autodocs:
cd gen_pages ; \
$(PYTHON) gen_sysobj_pages.py
24 changes: 24 additions & 0 deletions CI/doc/README_doc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Doc Creation

Doc is create by leveraging Sphinx as the documentation engine. To create the entire documentation set you must generate the dynamic pages which document the system object APIs and the reference designs. Then the output targets can be run. Since doc gen requires sphinx and some plugins they need to be installed first and ideally in a virtual environment. The following commands will create a virtual environment and install the necessary packages:

```bash
python3 -m venv venv
source venv/bin/activate
pip install -r CI/doc/requirements_doc.txt
```

Next we can build the documentation. The following commands will build the documentation and place it in the *build* folder under the *CI/doc* folder:

```bash
make -C CI/doc gen_autodocs html
```

## Updating the System Object Documentation

The system object documentation is generated from the MATLAB code and comments, which requires use of MATLAB itself. By running the script gen_sysobj_doc.m within the *gen_pages* folder, it will create the necessary sysobjs.json file which sphinx will then use to create the individual component pages. This is done by running the following command from the root of the toolbox in MATLAB:

```matlab
cd CI/doc/gen_pages
gen_sysobj_doc
```
14 changes: 14 additions & 0 deletions CI/doc/gen_pages/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Define the Python interpreter
PYTHON = python

# Define the targets and their dependencies
all: gen_sysobj_pages gen_rd_svg gen_hdl_refdesigns

gen_sysobj_pages:
$(PYTHON) gen_sysobj_pages.py

gen_rd_svg:
$(PYTHON) gen_rd_svg.py

gen_hdl_refdesigns:
$(PYTHON) gen_hdl_refdesigns.py
19 changes: 19 additions & 0 deletions CI/doc/gen_pages/_templates/allsysobjs.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Hardware Interface APIs

Available hardware streaming interfaces in ToolboxCommon:
<!--
- [{{ obj }}](matlab:web([docroot '/3ptoolbox/analogdevicesinchighspeedconvertertoolboxtoolbox/doc/sysobjects/{{ obj }}/index.html']))
-->

```{eval-rst}
.. toctree::
:maxdepth: 1
{% for obj in devices %}
sysobjects/{{ obj }}
{%- endfor %}

```



Click on left table of contents for individual component pages.
94 changes: 94 additions & 0 deletions CI/doc/gen_pages/_templates/sysobj.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
{% block content %}
# {{ obj.name }}
<!-- <div class="sysobj_h1">{{ obj.name }}</div> -->

<!-- <div class="sysobj_top_desc">
Receive data from Analog Devices AD9361 transceiver
</div> -->

<!-- <div class="sysobj_desc_title">Description</div> -->

<div class="sysobj_desc_txt">
<span>
{{ obj.dec }}
</span>

</div>

<div class="sysobj_desc_title">Creation</div>

The class can be instantiated in the following way with and without property name value pairs.

```matlab
dev = {{ obj.name }}
dev = {{ obj.name }}(Name, Value)
```

<div class="sysobj_desc_title">Properties</div>

<div class="sysobj_desc_txt">
<span>
Unless otherwise indicated, properties are non-tunable, which means you cannot change their values after calling the object. Objects lock when you call them, and the release function unlocks them.
<br><br>
If a property is tunable, you can change its value at any time.
<br><br>
For more information on changing property values, see <a href="https://www.mathworks.com/help/matlab/matlab_prog/system-design-in-matlab-using-system-objects.html">System Design in MATLAB Using System Objects.</a>
</span>
</div>
<br>


{% for prop in obj.props %}
:::{dropdown} {{ prop.prop_name }}
{{ prop.prop_description }}
:::
{% endfor -%}




{% endblock %}

<br>
<div class="sysobj_desc_title">Example Usage</div>

```matlab
{% if obj.type == "Tx" %}
%% Configure device
tx = {{ obj.name }};
tx.uri = 'ip:analog.local';
{% if obj.name == "adi.ADRV9002.Tx" %}
tx.CenterFrequencyChannel0 = 1e9;
{% else %}
tx.CenterFrequency = 1e9;
{% endif %}
tx.DataSource = 'DMA';
tx.EnableCyclicBuffers = true;
tx.EnabledChannels = 1;
%% Generate tone
amplitude = 2^15; frequency = 0.12e6;
swv1 = dsp.SineWave(amplitude, frequency);
swv1.ComplexOutput = true;
swv1.SamplesPerFrame = 2^14;
swv1.SampleRate = tx.SamplingRate;
y = swv1();
% Send
tx(y);
{% else %}
%% Rx set up
rx = {{ obj.name }}('uri','ip:analog.local');
{% if obj.name == "adi.ADRV9002.Rx" %}
rx.CenterFrequencyChannel0 = 1e9;
{% else %}
rx.CenterFrequency = 1e9;
{% endif %}
rx.EnabledChannels = 1;
%% Run
for k=1:10
valid = false;
while ~valid
[out, valid] = rx();
end
end
{% endif %}
```
74 changes: 74 additions & 0 deletions CI/doc/gen_pages/gen_sysobj_doc.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
[filepath,name,ext] = fileparts(mfilename('fullpath'));
cd(filepath);
cd('..');
files = dir(filepath);

mfiledir = fullfile('adi');
docdir = fullfile('doc');

rootClasses = {...
{'Attribute'},...
{'DDS'},...
{'Tx'},...
{'BufferADI'},...
{'Channel'},...
{'DDS'},...
{'DebugAttribute'},...
{'DeviceAttribute'},...
{'RegisterReadWrite'},...
{'Rx'},...
{'RxTx'},...
{'Sensor'},...
};

all_devs = [];
for ii = 1:numel(rootClasses)
part = rootClasses{ii}{1};
all_props = [];
dotmfilename = strcat(part);
props = properties(dotmfilename);
for prop = 1:length(props)

if props{prop} == "enIO"
continue;
end
pdoc = help(strcat(dotmfilename,'.',props{prop}));

pdocs = strsplit(pdoc,'\n');
prop_title = pdocs{1};
prop_description = strip(replace(strjoin(pdocs(2:end),'\n'),'\n',''));
prop_description = int32(prop_description);
prop_description(prop_description==10) = [];
prop_description(prop_description==13) = [];
prop_description = char(prop_description);
prop_description = replace(prop_description,' ',' ');
prop_description = replace(prop_description,' ',' ');

s = struct('prop_name',props{prop},...
'prop_title',prop_title,...
'prop_description',prop_description);
all_props = [all_props,s];
end
top_doc = help(dotmfilename);
top_doc = strsplit(top_doc,'\n');
top_doc = replace(top_doc,'\n','<br>');
top_doc = strjoin(top_doc(2:end),'<br>');

% top_doc = strip(replace(top_doc,'\n',''));
% top_doc = int32(top_doc);
% top_doc(top_doc==10) = [];
% top_doc(top_doc==13) = [];
% top_doc = char(top_doc);
% top_doc = replace(top_doc,' ',' ');
% top_doc = replace(top_doc,' ',' ');


oname = struct('name',dotmfilename, 'dec',top_doc, 'props',all_props);
all_devs = [all_devs, oname];
end
%%
jsonText = jsonencode(all_devs,'PrettyPrint',true);
fid = fopen('docs/sysobjs.json', 'w');
fprintf(fid, '%s', jsonText);
fclose(fid);

101 changes: 101 additions & 0 deletions CI/doc/gen_pages/gen_sysobj_pages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
from jinja2 import Environment, FileSystemLoader
import os
import json


def gen_sys_obj_pages(matlab):

folder_of_this_file = os.path.dirname(__file__)

source_folder = os.path.join(folder_of_this_file, "..", "source")

_generated_folder = os.path.join(folder_of_this_file, "..", "source", "_generated")
if not os.path.exists(_generated_folder):
os.mkdir(_generated_folder)

sys_obj_gen_folder = os.path.join(_generated_folder, "sysobjects")
if not os.path.exists(sys_obj_gen_folder):
os.mkdir(sys_obj_gen_folder)


template_filename = "sysobj.html"

# Data for template
if not os.path.exists("sysobjs.json"):
raise Exception("sysobjs.json not found. Run gen_sysobj_doc.m first.")
with open("sysobjs.json") as f:
objs = json.load(f)

# Import template
loc = os.path.dirname(__file__)
loc = os.path.join(loc, "_templates")
file_loader = FileSystemLoader(loc)
env = Environment(loader=file_loader)

loc = os.path.join(template_filename)
template = env.get_template(loc)

devices = {}

def cleanup(obj):

obj["dec"] = obj["dec"].replace("192.168.2.1", "ip:192.168.2.1")
d = obj["dec"]
ol = []
for d in obj["dec"].split("<br>"):

if "See also" in d:
continue
if "Documentation for" in d:
continue
if "doc adi." in d:
continue

ol.append(d)

obj["dec"] = "<br>".join(ol)
if ".Rx" in obj["name"]:
obj["type"] = "Rx"
else:
obj["type"] = "Tx"

return obj

for obj in objs:
print("Generating doc page for", obj["name"])
# Render template
obj = cleanup(obj)
output = template.render(obj=obj, disable_nav=matlab)
# Write output
output_filename = os.path.join(sys_obj_gen_folder, f"{obj['name']}.md")
# output_filename = f"sysobjects/{obj['name']}.md"
loc = os.path.join(output_filename)
f = open(loc, "w")
f.write(output)
f.close()
devices[obj["name"]] = output_filename

# Create allsysobjs.md
loc = os.path.join("allsysobjs.tmpl")
template = env.get_template(loc)
output = template.render(devices=devices, disable_nav=matlab)

loc = os.path.join(_generated_folder, "objects.md")
with open(loc, "w") as f:
f.write(output)


if matlab:
# Generate index for objs
loc = os.path.join("allsysobjs.tmpl")
template = env.get_template(loc)
output = template.render(devices=devices, disable_nav=matlab)

loc = os.path.join("objects.md")
with open(loc, "w") as f:
f.write(output)

return devices

if __name__ == "__main__":
gen_sys_obj_pages(False)
Loading
Loading