diff --git a/platforms/arduino/docs/.gitignore b/platforms/arduino/docs/.gitignore new file mode 100644 index 000000000..45d4c0378 --- /dev/null +++ b/platforms/arduino/docs/.gitignore @@ -0,0 +1 @@ +btstack-arduino/ diff --git a/platforms/arduino/docs/Makefile b/platforms/arduino/docs/Makefile new file mode 100644 index 000000000..d9cf91027 --- /dev/null +++ b/platforms/arduino/docs/Makefile @@ -0,0 +1,3 @@ +all: + ./update_listings.py + mkdocs build --clean diff --git a/platforms/arduino/docs/docs/examples/.gitignore b/platforms/arduino/docs/docs/examples/.gitignore new file mode 100644 index 000000000..efcc288f8 --- /dev/null +++ b/platforms/arduino/docs/docs/examples/.gitignore @@ -0,0 +1 @@ +generated.md diff --git a/platforms/arduino/docs/docs/examples/intro.md b/platforms/arduino/docs/docs/examples/intro.md new file mode 100644 index 000000000..e69de29bb diff --git a/platforms/arduino/docs/docs/index.md b/platforms/arduino/docs/docs/index.md new file mode 100644 index 000000000..e69de29bb diff --git a/platforms/arduino/docs/generated.md b/platforms/arduino/docs/generated.md new file mode 100644 index 000000000..48d8473c1 --- /dev/null +++ b/platforms/arduino/docs/generated.md @@ -0,0 +1,20 @@ + + + +- iBeacon example: + + - [iBeacon](#section:iBeacon): iBeaconScanner. + +- ANCS example: + + - [ANCS](#section:ANCS): . + +- LE Central example: + + - [LECentral](#section:LECentral): . + +- LE Peripheral example: + + - [LEPeripheral](#section:LEPeripheral): . + + diff --git a/platforms/arduino/docs/mkdocs.yml b/platforms/arduino/docs/mkdocs.yml new file mode 100644 index 000000000..04e71c1fd --- /dev/null +++ b/platforms/arduino/docs/mkdocs.yml @@ -0,0 +1,7 @@ +site_name: BTstack Arduino Manual +site_dir: btstack-arduino +pages: +- [index.md, Welcome] +- [examples/intro.md, Examples] +- [examples/generated.md, List of Examples] +theme: readthedocs diff --git a/platforms/arduino/docs/update_listings.py b/platforms/arduino/docs/update_listings.py new file mode 100755 index 000000000..b6b3ef9a6 --- /dev/null +++ b/platforms/arduino/docs/update_listings.py @@ -0,0 +1,331 @@ +#!/usr/bin/env python +import getopt +import re +import sys + +# Defines the names of example groups. Preserves the order in which the example groups will be parsed. +list_of_groups = ["iBeacon", "ANCS", "LE Central", "LE Peripheral"] + +# Defines which examples belong to a group. Example is defined as [example file, example title]. +list_of_examples = { + "iBeacon": [["iBeacon", "iBeaconScanner"]], + "ANCS": [["ANCS"]], + "LE Central": [["LECentral"]], + "LE Peripheral": [["LEPeripheral"]], +} + +lst_header = """ +""" + +lst_ending = """ +""" + +examples_header = """ +""" + +example_item = """ + - [EXAMPLE_TITLE](#section:EXAMPLE_LABEL): EXAMPLE_DESC. +""" +example_section = """ + +## EXAMPLE_TITLE: EXAMPLE_DESC + + +""" +example_subsection = """ +### SECTION_TITLE +""" + +listing_start = """ + + + + +""" + +listing_ending = """ + +""" + + +def replacePlaceholder(template, title, lable): + snippet = template.replace("API_TITLE", title).replace("API_LABEL", lable) + return snippet + + +def latexText(text, ref_prefix): + if not text: + return "" + brief = text.replace(" in the BTstack manual","") + + refs = re.match('.*(Listing\s+)(\w+).*',brief) + if refs: + brief = brief.replace(refs.group(1), "[code snippet below]") + brief = brief.replace(refs.group(2), "(#"+ref_prefix+":" + refs.group(2)+")") + + refs = re.match('.*(Section\s+)(\w+).*',brief) + if refs: + brief = brief.replace(refs.group(1), "[here]") + brief = brief.replace(refs.group(2), "(#section:"+refs.group(2)+")") + + return brief + + +def isEmptyCommentLine(line): + return re.match('(\s*\*\s*)\n',line) + + +def isCommentLine(line): + return re.match('(\s*\*\s*).*',line) + + +def isEndOfComment(line): + return re.match('\s*\*/.*', line) + + +def isNewItem(line): + return re.match('(\s*\*\s*\-\s*)(.*)',line) + + +def isTextTag(line): + return re.match('.*(@text).*', line) + + +def isItemizeTag(line): + return re.match("(\s+\*\s+)(-\s)(.*)", line) + + +def processTextLine(line, ref_prefix): + if isTextTag(line): + text_line_parts = re.match(".*(@text)(.*)", line) + return " " + latexText(text_line_parts.group(2), ref_prefix) + + if isItemizeTag(line): + text_line_parts = re.match("(\s*\*\s*\-\s*)(.*)", line) + return "\n- " + latexText(text_line_parts.group(2), ref_prefix) + + text_line_parts = re.match("(\s+\*\s+)(.*)", line) + if text_line_parts: + return " " + latexText(text_line_parts.group(2), ref_prefix) + return "" + +def getExampleTitle(example_path): + example_title = '' + with open(example_path, 'rb') as fin: + for line in fin: + parts = re.match('.*(EXAMPLE_START)\((.*)\):\s*(.*)(\*/)?\n',line) + if parts: + example_title = parts.group(3).replace("_","\_") + continue + return example_title + +class State: + SearchExampleStart = 0 + SearchListingStart = 1 + SearchListingPause = 2 + SearchListingResume = 3 + SearchListingEnd = 4 + SearchItemizeEnd = 5 + ReachedExampleEnd = 6 + +text_block = '' +itemize_block = '' + +def writeTextBlock(aout, lstStarted): + global text_block + if text_block and not lstStarted: + aout.write(text_block) + text_block = '' + +def writeItemizeBlock(aout, lstStarted): + global itemize_block + if itemize_block and not lstStarted: + aout.write(itemize_block + "\n\n") + itemize_block = '' + +def writeListings(aout, infile_name, ref_prefix): + global text_block, itemize_block + itemText = None + state = State.SearchExampleStart + code_in_listing = "" + code_identation = " " + skip_code = 0 + + with open(infile_name, 'rb') as fin: + for line in fin: + if state == State.SearchExampleStart: + parts = re.match('.*(EXAMPLE_START)\((.*)\):\s*(.*)(\*/)?\n',line) + if parts: + lable = parts.group(2).replace("_","") + title = latexText(parts.group(2), ref_prefix) + desc = latexText(parts.group(3), ref_prefix) + aout.write(example_section.replace("EXAMPLE_TITLE", title).replace("EXAMPLE_DESC", desc).replace("EXAMPLE_LABEL", lable)) + state = State.SearchListingStart + continue + + # detect @section + section_parts = re.match('.*(@section)\s*(.*)(:?\s*.?)\*?/?\n',line) + if section_parts: + aout.write("\n" + example_subsection.replace("SECTION_TITLE", section_parts.group(2))) + continue + + # detect @subsection + subsection_parts = re.match('.*(@section)\s*(.*)(:?\s*.?)\*?/?\n',line) + if section_parts: + subsubsection = example_subsection.replace("SECTION_TITLE", section_parts.group(2)).replace('section', 'subsection') + aout.write("\n" + subsubsection) + continue + + if isTextTag(line): + text_block = text_block + "\n\n" + processTextLine(line, ref_prefix) + continue + + skip_code = 0 + lstStarted = state != State.SearchListingStart + if text_block or itemize_block: + if isEndOfComment(line) or isEmptyCommentLine(line): + skip_code = 1 + if itemize_block: + # finish itemize + writeItemizeBlock(aout, lstStarted) + else: + if isEmptyCommentLine(line): + text_block = text_block + "\n\n" + + else: + writeTextBlock(aout, lstStarted) + + + else: + if isNewItem(line) and not itemize_block: + skip_code = 1 + # finish text, start itemize + writeTextBlock(aout, lstStarted) + itemize_block = "\n " + processTextLine(line, ref_prefix) + continue + if itemize_block: + skip_code = 1 + itemize_block = itemize_block + processTextLine(line, ref_prefix) + elif isCommentLine(line): + # append text + skip_code = 1 + text_block = text_block + processTextLine(line, ref_prefix) + else: + skip_code = 0 + #continue + + if state == State.SearchListingStart: + parts = re.match('.*(LISTING_START)\((.*)\):\s*(.*)(\s+\*/).*',line) + + if parts: + lst_lable = parts.group(2).replace("_","") + lst_caption = latexText(parts.group(3), ref_prefix) + listing = listing_start.replace("LISTING_CAPTION", lst_caption).replace("FILE_NAME", ref_prefix).replace("LISTING_LABEL", lst_lable) + if listing: + aout.write("\n" + listing) + state = State.SearchListingEnd + continue + + if state == State.SearchListingEnd: + parts_end = re.match('.*(LISTING_END).*',line) + parts_pause = re.match('.*(LISTING_PAUSE).*',line) + end_comment_parts = re.match('.*(\*/)\s*\n', line); + + if parts_end: + aout.write(code_in_listing) + code_in_listing = "" + aout.write(listing_ending) + state = State.SearchListingStart + writeItemizeBlock(aout, 0) + writeTextBlock(aout, 0) + elif parts_pause: + code_in_listing = code_in_listing + code_identation + "...\n" + state = State.SearchListingResume + elif not end_comment_parts: + # aout.write(line) + if not skip_code: + code_in_listing = code_in_listing + code_identation + line.replace(" ", " ") + continue + + if state == State.SearchListingResume: + parts = re.match('.*(LISTING_RESUME).*',line) + if parts: + state = State.SearchListingEnd + continue + + parts = re.match('.*(EXAMPLE_END).*',line) + if parts: + if state != State.SearchListingStart: + print "Formating error detected" + writeItemizeBlock(aout, 0) + writeTextBlock(aout, 0) + state = State.ReachedExampleEnd + print "Reached end of the example" + + +# write list of examples +def processExamples(examples_folder, examples_ofile): + with open(examples_ofile, 'w') as aout: + for group_title in list_of_groups: + if not list_of_examples.has_key(group_title): continue + examples = list_of_examples[group_title] + for example in examples: + example_path = examples_folder + example[0] + "/" + example[0] + ".ino" + example_title = getExampleTitle(example_path) + example.append(example_title) + + aout.write(examples_header) + aout.write("\n\n"); + + for group_title in list_of_groups: + if not list_of_examples.has_key(group_title): continue + examples = list_of_examples[group_title] + + group_title = group_title + " example" + if len(examples) > 1: + group_title = group_title + "s" + group_title = group_title + ":" + + aout.write("- " + group_title + "\n"); + for example in examples: + ref_prefix = example[0].replace("_", "") + title = latexText(example[0], ref_prefix) + desc = latexText(example[1], ref_prefix) + aout.write(example_item.replace("EXAMPLE_TITLE", title).replace("EXAMPLE_DESC", desc).replace("EXAMPLE_LABEL", ref_prefix)) + aout.write("\n") + aout.write("\n") + + for group_title in list_of_groups: + if not list_of_examples.has_key(group_title): continue + examples = list_of_examples[group_title] + + for example in examples: + file_name = examples_folder + example[0] + "/" + example[0] + ".ino" + writeListings(aout, file_name, example[0].replace("_","")) + + +def main(argv): + btstack_folder = "../../../" + docs_folder = "docs/examples/" + inputfolder = btstack_folder + "platforms/arduino/examples/" + outputfile = docs_folder + "generated.md" + + try: + opts, args = getopt.getopt(argv,"hiso:",["ifolder=","ofile="]) + except getopt.GetoptError: + print 'update_listings.py [-i ] [-o ]' + sys.exit(2) + for opt, arg in opts: + if opt == '-h': + print 'update_listings.py [-i ] [-s] [-o ]' + sys.exit() + elif opt in ("-i", "--ifolder"): + inputfolder = arg + elif opt in ("-o", "--ofile"): + outputfile = arg + print 'Input folder is ', inputfolder + print 'Output file is ', outputfile + processExamples(inputfolder, outputfile) + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/platforms/arduino/docs/upload_site_sftp.sh b/platforms/arduino/docs/upload_site_sftp.sh new file mode 100755 index 000000000..1ebdc1cf6 --- /dev/null +++ b/platforms/arduino/docs/upload_site_sftp.sh @@ -0,0 +1,20 @@ +#!/bin/bash +USAGE="Usage: upload_site_sftp.sh host path user" + +# command line checks, bash +if [ $# -ne 3 ]; then + echo ${USAGE} + exit 0 +fi +host=$1 +path=$2 +user=$3 + +echo Uploading generated docs to ${host}/${path}/btstack with user ${user} + +# SFTP is very peculiar: recursive put only works for a single directory +sftp ${user}@${host} << EOF + mkdir ${path}/btstack + put -r btstack ${path} + quit +EOF