From a310b79799ef78dc02fd8d5e574aea91ec69a6a7 Mon Sep 17 00:00:00 2001 From: Stephen Hawes Date: Fri, 24 Mar 2023 15:51:51 -0400 Subject: [PATCH] setting up ci exports --- .github/workflows/export-bom.yaml | 111 ++++++++ .github/workflows/export-ecad.yaml | 118 +++++++++ .github/workflows/export-mcad.yaml | 98 +++++++ .github/workflows/scripts/export-bom.py | 120 +++++++++ .github/workflows/scripts/export-csm.py | 127 +++++++++ .github/workflows/scripts/export-stl.py | 196 ++++++++++++++ .github/workflows/scripts/generate-stl-img.py | 42 +++ .../scripts/kibot/config-2layer.kibot.yaml | 215 +++++++++++++++ .../scripts/kibot/config-4layer.kibot.yaml | 247 ++++++++++++++++++ .../scripts/kibot/config-blank.kibot.yaml | 95 +++++++ .../workflows/scripts/kibot/config.kibot.yaml | 229 ++++++++++++++++ 11 files changed, 1598 insertions(+) create mode 100644 .github/workflows/export-bom.yaml create mode 100644 .github/workflows/export-ecad.yaml create mode 100644 .github/workflows/export-mcad.yaml create mode 100644 .github/workflows/scripts/export-bom.py create mode 100644 .github/workflows/scripts/export-csm.py create mode 100644 .github/workflows/scripts/export-stl.py create mode 100644 .github/workflows/scripts/generate-stl-img.py create mode 100644 .github/workflows/scripts/kibot/config-2layer.kibot.yaml create mode 100644 .github/workflows/scripts/kibot/config-4layer.kibot.yaml create mode 100644 .github/workflows/scripts/kibot/config-blank.kibot.yaml create mode 100644 .github/workflows/scripts/kibot/config.kibot.yaml diff --git a/.github/workflows/export-bom.yaml b/.github/workflows/export-bom.yaml new file mode 100644 index 0000000..fb309b4 --- /dev/null +++ b/.github/workflows/export-bom.yaml @@ -0,0 +1,111 @@ +name: Export BOM + +on: + workflow_dispatch: + release: + types: [ published ] + +jobs: + generate-bom: + name: Generate BOM + runs-on: ubuntu-20.04 + steps: + - name: Generate Short SHA Environment Variable + run: echo "SHORT_SHA=`echo ${GITHUB_SHA} | cut -c1-8`" >> $GITHUB_ENV + + - name: Install Python + run: | + sudo apt install python3 + + - name: Download repository + uses: actions/checkout@v2 + + - name: Generate BOM HTML Page for Release + if: github.event_name == 'release' + run: | + python3 .github/workflows/scripts/export-bom.py ${{ github.event.release.tag_name }} + + - name: Generate BOM HTML Page for Workflow Dispatch + if: github.event_name != 'release' + run: | + python3 .github/workflows/scripts/export-bom.py ${SHORT_SHA} + + - name: Install FreeCAD Python library + run: | + sudo apt -qq update + sudo apt-get -qq -y install python3 + sudo apt-get -qq -y install python3-pip + sudo apt-get -qq -y install qt5-default + python3 -m pip install --upgrade pip + pip install pyside2==5.12.6 + + - name: Fetch FreeCAD + uses: dsaltares/fetch-gh-release-asset@master + with: + repo: "FreeCAD/FreeCAD" + version: "tags/0.19.2" + file: "FreeCAD_0.19-24291-Linux-Conda_glibc2.12-x86_64.AppImage" + target: "FreeCAD.AppImage" + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Install FreeCad + run: | + sudo chown runner:docker FreeCAD.AppImage + pwd + chmod +x FreeCAD.AppImage + ./FreeCAD.AppImage --appimage-extract > /dev/null + + - name: Generate STL files + run: | + cd .github/workflows/scripts + python3 export-stl.py + ls -al + + - name: Install OpenSCAD + run: | + sudo add-apt-repository ppa:openscad/releases + sudo apt-get update + sudo apt-get install openscad + + - name: Generate STL Images for Release + if: github.event_name == 'release' + run: | + sudo apt-get install xvfb + Xvfb :5 -screen 0 800x600x24 & + export DISPLAY=:5 + python3 .github/workflows/scripts/generate-stl-img.py ${{ github.event.release.tag_name }} + + + - name: Generate STL Images for Workflow Dispatch + if: github.event_name != 'release' + run: | + sudo apt-get install xvfb + Xvfb :5 -screen 0 800x600x24 & + export DISPLAY=:5 + python3 .github/workflows/scripts/generate-stl-img.py ${SHORT_SHA} + + - name: Zip BOM Directory for Workflow Dispatch + if: github.event_name != 'release' + run: | + zip -r LumenPnP-BOM.zip LumenPnP-${SHORT_SHA} LumenPnP-${SHORT_SHA}/img + ls -al + + - name: Zip BOM Directory for Release + if: github.event_name == 'release' + run: | + zip -r LumenPnP-BOM-${{ github.event.release.tag_name }}.zip LumenPnP-${{ github.event.release.tag_name }} LumenPnP-${{ github.event.release.tag_name }}/img + + - name: upload BOM results + if: github.event_name != 'release' + uses: actions/upload-artifact@v2 + with: + name: LumenPnP-BOM + path: LumenPnP-BOM.zip + + - name: Upload Artifacts to Release + uses: softprops/action-gh-release@v1 + if: github.event_name == 'release' + with: + files: | + LumenPnP-BOM-${{ github.event.release.tag_name }}.zip + diff --git a/.github/workflows/export-ecad.yaml b/.github/workflows/export-ecad.yaml new file mode 100644 index 0000000..99a6204 --- /dev/null +++ b/.github/workflows/export-ecad.yaml @@ -0,0 +1,118 @@ +name: Export ECAD +on: + workflow_dispatch: + release: + types: [ published ] + +jobs: + export-ecad: + name: Export ECAD + runs-on: ubuntu-latest + container: setsoft/kicad_auto:dev_k6 + + steps: + + - name: Generate Short SHA Environment Variable + run: echo "SHORT_SHA=`echo ${GITHUB_SHA} | cut -c1-8`" >> $GITHUB_ENV + + - name: Update system repositories, Install Required Libraries and Initialize git-lfs + run: | + apt update + apt -y install git git-lfs zip librsvg2-bin imagemagick + git lfs install + + - name: Checkout Repository + uses: actions/checkout@v2 + with: + lfs: true + + - name: Extract branch name + shell: bash + run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})" + id: extract_branch + + - name: Update the PCBs with on default branch with git hash + if: github.event_name == 'release' || steps.extract_branch.outputs.branch == env.main_branch + run: | + export COMMIT=$(git rev-parse --short HEAD) + echo "COMMIT = ${COMMIT}" + echo "ref: ${{ github.ref }}" + echo "default: ${{ env.default }}" + sed -i "s!<>!${COMMIT}!" pnp/pcb/mobo/mobo.kicad_pcb + sed -i "s!<>!${COMMIT}!" pnp/pcb/ring-light/ringLight.kicad_pcb + sed -i "s!<>!${COMMIT}!" pnp/pcb/staging-plate/staging-plate.kicad_pcb + sed -i "s!<>!${COMMIT}!" pnp/pcb/datum/datum.kicad_pcb + sed -i "s!<>!${COMMIT}!" pnp/pcb/ftp/ftp.kicad_pcb + + - name: Update the PCBs with the git hash and BETA. + if: steps.extract_branch.outputs.branch != env.main_branch + run: | + export COMMIT=$(git rev-parse --short HEAD) + echo "COMMIT = ${COMMIT}" + sed -i "s!<>!BETA-${COMMIT}!" pnp/pcb/mobo/mobo.kicad_pcb + sed -i "s!<>!BETA-${COMMIT}!" pnp/pcb/ring-light/ringLight.kicad_pcb + sed -i "s!<>!BETA-${COMMIT}!" pnp/pcb/staging-plate/staging-plate.kicad_pcb + sed -i "s!<>!BETA-${COMMIT}!" pnp/pcb/datum/datum.kicad_pcb + sed -i "s!<>!BETA-${COMMIT}!" pnp/pcb/ftp/ftp.kicad_pcb + + - name: Generate Mobo Export Files + run: | + cd pcb/mobo + rm -rf mobo/ + kibot -c ../../../.github/workflows/scripts/kibot/config-4layer.kibot.yaml -e mobo.kicad_sch -b mobo.kicad_pcb -d mobo + zip -r -j mobo.zip mobo/ + + - name: Generate Slot Export Files + run: | + cd pcb/feederFloor + rm -rf feeder-floor/ + kibot -c ../../../.github/workflows/scripts/kibot/config-2layer.kibot.yaml -e feederFloor.kicad_sch -b feederFloor.kicad_pcb -d slot + zip -r -j slot.zip slot/ + + - name: Generate Indexing Wheel Export Files + run: | + cd pcb/indexingWheel + rm -rf indexing-wheel/ + kibot -c ../../../.github/workflows/scripts/kibot/config-2layer.kibot.yaml -e indexingWheel.kicad_sch -b indexingWheel.kicad_pcb -d indexing-wheel + zip -r -j indexing-wheel.zip indexing-wheel/ + + - name: Generate Light Diffusion Export Files + run: | + cd pcb/light-diffusion + rm -rf light-diffusion/ + kibot -c ../../../.github/workflows/scripts/kibot/config-2layer.kibot.yaml -e light-diffusion.kicad_sch -b light-diffusion.kicad_pcb -d light-diffusion + zip -r -j light-diffusion.zip light-diffusion/ + + - name: Generate Photon 8mm Fiducial Export Files + run: | + cd pcb/photon-8mm-fid + rm -rf photon-8mm-fid/ + kibot -c ../../../.github/workflows/scripts/kibot/config-2layer.kibot.yaml -e photon-8mm-fid.kicad_sch -b photon-8mm-fid.kicad_pcb -d photon-8mm-fid + zip -r -j photon-8mm-fid.zip photon-8mm-fid/ + + - name: Zip PCB Export Files for Artifacts + run: | + cd pcb/ + zip -r -j Feeder-PCBs.zip photon-8mm-fid/photon-8mm-fid.zip light-diffusion/light-diffusion.zip indexingWheel/indexing-wheel.zip feederFloor/slot.zip mobo/mobo.zip + + - name: Zip PCB Export Files for Release + run: | + cd pcb/ + zip -r -j Feeder-PCBs-${{ github.event.release.tag_name }}.zip photon-8mm-fid/photon-8mm-fid.zip light-diffusion/light-diffusion.zip indexingWheel/indexing-wheel.zip feederFloor/slot.zip mobo/mobo.zip + + - name: Upload PCB Export Files as Artifacts + uses: actions/upload-artifact@v2 + with: + name: Feeder-PCBs.zip + path: pcb/Feeder-PCBs.zip + if-no-files-found: error + retention-days: 60 + + - name: Upload Artifacts to Release + uses: softprops/action-gh-release@v1 + if: github.event_name == 'release' + with: + files: | + pcb/Feeder-PCBs-${{ github.event.release.tag_name }}.zip + + diff --git a/.github/workflows/export-mcad.yaml b/.github/workflows/export-mcad.yaml new file mode 100644 index 0000000..0394e96 --- /dev/null +++ b/.github/workflows/export-mcad.yaml @@ -0,0 +1,98 @@ +name: Export MCAD +on: + release: + types: [ published ] + workflow_dispatch: + + +jobs: + export-mcad: + name: Export MCAD + runs-on: ubuntu-20.04 + + steps: + + - name: Generate Short SHA Environment Variable + run: echo "SHORT_SHA=`echo ${GITHUB_SHA} | cut -c1-8`" >> $GITHUB_ENV + + - name: Download repository + uses: actions/checkout@v2 + + - name: Install FreeCAD Python library + run: | + sudo apt -qq update + sudo apt-get -qq -y install python3 + sudo apt-get -qq -y install python3-pip + sudo apt-get -qq -y install qt5-default + python3 -m pip install --upgrade pip + pip install pyside2==5.12.6 + + - name: Fetch FreeCAD + uses: dsaltares/fetch-gh-release-asset@master + with: + repo: "FreeCAD/FreeCAD" + version: "tags/0.19.2" + file: "FreeCAD_0.19-24291-Linux-Conda_glibc2.12-x86_64.AppImage" + target: "FreeCAD.AppImage" + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Install FreeCad + run: | + sudo chown runner:docker FreeCAD.AppImage + pwd + chmod +x FreeCAD.AppImage + ./FreeCAD.AppImage --appimage-extract > /dev/null + + - name: Generate DXF files + run: | + cd .github/workflows/scripts + python3 export-csm.py + pwd + ls -al + cd csm-export + ls -al + env: + LD_LIBRARY_PATH: /home/runner/work/feeder/feeder/squashfs-root/usr/lib + + - name: Generate STL files + run: | + cd .github/workflows/scripts + python3 export-stl.py + + - name: Compress STL and DXF files for Release + if: github.event_name == 'release' + run: | + cd .github/workflows/scripts/stl-export + zip -9 -j /home/runner/work/feeder/feeder/Feeder-STLs-${{ github.event.release.tag_name }}.zip *.stl + cd ../csm-export + + - name: Compress STL and DXF files for Artifacts + if: github.event_name != 'release' + run: | + cd .github/workflows/scripts/stl-export + zip -9 -j ~/Feeder-STLs.zip *.stl + cd /home/runner/work/feeder/feeder/.github/workflows/scripts/csm-export + + - name: Upload STLs as Artifacts + if: github.event_name != 'release' + uses: actions/upload-artifact@v2 + with: + name: Feeder-STLs + path: ~/Feeder-STLs.zip + if-no-files-found: error + retention-days: 60 + + - name: Upload DXFs as Artifacts + if: github.event_name != 'release' + uses: actions/upload-artifact@v2 + with: + name: Feeder-DXFs + path: ~/Feeder-DXFs.zip + if-no-files-found: warn + retention-days: 60 + + - name: Upload STLs and DXFs Release + uses: softprops/action-gh-release@v1 + if: github.event_name == 'release' + with: + files: Feeder-STLs-${{ github.event.release.tag_name }}.zip diff --git a/.github/workflows/scripts/export-bom.py b/.github/workflows/scripts/export-bom.py new file mode 100644 index 0000000..e2c2598 --- /dev/null +++ b/.github/workflows/scripts/export-bom.py @@ -0,0 +1,120 @@ +#!/usr/bin/python3 + +import os +from platform import release +import sys +import csv + +# make directory for bom html file and assets +dirName = "Feeder-" + sys.argv[1] +if not os.path.exists(dirName): + os.mkdir(dirName) + print("Directory " , dirName , " Created ") +else: + print("Directory " , dirName , " already exists") + + +# make html file that will become our BOM +f = open(dirName + "/bom_" + sys.argv[1] + ".html", "w") +f.write(""" + + + + + +

+""") + +#write page title based on script +f.write("LumenPnP BOM " + sys.argv[1]) + +#write the beginning of table, and row for header +f.write("

Download the Source Here

") + +with open('bom.csv') as bom: + csv_reader = csv.reader(bom, delimiter=',') + line_count = 0 + + for row in csv_reader: + column = 0 + + if line_count == 0: #if header row, just print what's there + f.write("") + while column < len(row): + f.write("") + column += 1 + else: #if content row + if row[6] != "": + f.write("") + else: + f.write("") + + while column < len(row): + + # handling images + if column == 1 and row[3] != "FDM": + f.write("") + elif column == 1 and row[3] == "FDM": + f.write("") + + # handling links + elif (column == 4 or column == 5) and row[3] != "FDM" and row[column] != "": + f.write("") + # all other cells + else: + f.write("") + column += 1 + f.write("") + line_count += 1 + +f.write("
" + row[column] + "
Link" + row[column] + "
") + + +f.close() \ No newline at end of file diff --git a/.github/workflows/scripts/export-csm.py b/.github/workflows/scripts/export-csm.py new file mode 100644 index 0000000..6161baf --- /dev/null +++ b/.github/workflows/scripts/export-csm.py @@ -0,0 +1,127 @@ +#!/usr/bin/python3 +import math +import os +import sys +import traceback +from pathlib import Path +from typing import List +import time + +freecad_paths = [ + '/home/runner/work/feeder/feeder/squashfs-root/usr/lib', # For CI when using AppImage + '/usr/lib/freecad/lib/', # For CI + '/usr/lib/freecad-daily-python3/lib/', # For Ubuntu + '/usr/lib64/freecad/lib64/', # For Fedora + '/Applications/FreeCAD.app/Contents/MacOS', # For Mac OS X + 'c:/Program Files/FreeCAD 0.18/bin/', # For Windows + 'c:/Program Files/FreeCAD 0.19/bin/', # For Windows +] + +# Font file relative to this python script +font_folder = '../../../lib/fonts' + +for path in freecad_paths: + if os.path.exists(path): + print(f"Added possible FreeCAD path: {path}") + sys.path.append(path) + +# Must be after sys.path.append above... +import FreeCAD +import importDXF + +print('Python version:',sys.version,'FreeCAD version:',FreeCAD.Version()) + +def process_csm_file(cad_file: Path, output_folder: Path): + print("Processing " + cad_file.name) + doc = FreeCAD.open(str(cad_file.absolute())) + + name = cad_file.name[:-6] + + # name_options = [obj.String for obj in doc.Objects if + # obj.isDerivedFrom("Part::Part2DObject") and obj.Label == "PN"] + # if name_options: + # name = name_options[0] + # else: + # # If there is no part number embossed throw error + # raise ValueError("Part " + cad_file.name + " doesn't have a ShapeString called PN for part number emboss") + + output_file=os.path.join(output_folder,name+'.dxf') + print("Output file "+output_file) + #output_file=os.path.join(output_folder,os.path.splitext(os.path.basename(cad_file))[0]+'.dxf') + + # if cad_file.name[:8] != name[:8]: + # # STL model file name does not match the part number embedded in the file + # raise ValueError( + # "Part " + cad_file.name[:8] + " doesn't match the part number in the FreeCad model - " + name[:8]) + + body = [obj for obj in doc.Objects if obj.Label == "Body"] + + if len(body) == 0: + print(f"Cannot find body object. {len(doc.Objects)} objects present") + for obj in doc.Objects: + print(f"- {obj.Label}") + + raise Exception(f"Object named 'Body' not found in model {cad_file.name}") + + body = body[0] + + # Find font references in the model and ensure they point to the correct font file + fonts = [obj for obj in doc.Objects if + obj.isDerivedFrom("Part::Part2DObject") and hasattr(obj, "FontFile")] + + for obj in fonts: + font_file_property = obj.getPropertyByName('FontFile') + new_font_file = os.path.join(font_folder, os.path.split(font_file_property)[1]) + if not os.path.isfile(new_font_file): + raise FileNotFoundError(f"Cannot find font file {new_font_file}") + + if new_font_file != font_file_property: + print(f"\tCorrected '{obj.Label}' font file name from {font_file_property}") + setattr(obj, "FontFile", new_font_file) + obj.touch() + + # Recompute the model to ensure its valid and does not contain broken references or edges + # Mark each object as "changed" + for obj in doc.Objects: + obj.touch() + + # Recompute the entire document + t0 = time.perf_counter() + doc.recompute(None, True, True) + t1 = time.perf_counter() + total = t1 - t0 + print(f"\tRecompute of model took {total:3f}s") + + __objs__=[] + __objs__.append(body) + importDXF.export(__objs__,output_file) + + +if __name__ == '__main__': + # Create output folder if needed + output_directory = Path('csm-export') + output_directory.mkdir(parents=True, exist_ok=True) + + csm_path = Path('../../../src/cad/CSM') + + files = sorted(csm_path.glob('*.FCStd')) + + exceptions: List[Exception] = [] + + for f in files: + try: + process_csm_file(f, output_directory) + except Exception as ex: + print(f"****") + print(f"\tAn error occurred while processing {str(f)}:") + print(f"\t{ex}") + traceback.print_exc() + print(f"****") + exceptions.append(ex) + + if exceptions: + verb = "was" if len(exceptions) == 1 else "were" + noun = "exception" if len(exceptions) == 1 else "exceptions" + print(f"There {verb} {len(exceptions)} {noun}") + + assert len(exceptions) == 0 diff --git a/.github/workflows/scripts/export-stl.py b/.github/workflows/scripts/export-stl.py new file mode 100644 index 0000000..1fdd473 --- /dev/null +++ b/.github/workflows/scripts/export-stl.py @@ -0,0 +1,196 @@ +#!/usr/bin/python3 +import math +import os +import sys +import traceback +from pathlib import Path +from typing import List +import time +import csv + +freecad_paths = [ + '/home/runner/work/feeder/feeder/squashfs-root/usr/lib', # For CI when using AppImage + '/usr/lib/freecad/lib/', # For CI + '/usr/lib/freecad-daily-python3/lib/', # For Ubuntu + '/usr/lib64/freecad/lib64/', # For Fedora + '/Applications/FreeCAD.app/Contents/MacOS', # For Mac OS X + 'c:/Program Files/FreeCAD 0.18/bin/', # For Windows + 'c:/Program Files/FreeCAD 0.19/bin/', # For Windows +] + +# Font file relative to this python script +font_folder = '../../../lib/fonts' + +for path in freecad_paths: + if os.path.exists(path): + print(f"Added possible FreeCAD path: {path}") + sys.path.append(path) + +import FreeCAD +import MeshPart + +print('Python version:') +print(sys.version) + +print('FreeCAD version:') +print(FreeCAD.Version()) + + +def process_file(cad_file: Path): + print("Processing " + cad_file.name) + + doc = FreeCAD.open(str(cad_file.absolute())) + + bom = open("../../../bom.csv") + + csv_reader = csv.reader(bom, delimiter=',') + count = 0 + flag = False + for row in csv_reader: + if row[0] == cad_file.name[:-6]: + print("found a part!") + print(row[0]) + count = row[2] + print(count) + if count != "-" and count != "": + flag = True + break + + #generates name with quantity + if flag: + name = cad_file.name[:-6] + "_" + count + "x" + print("making count name") + else: + name = cad_file.name[:-6] + print("making no count name") + + + # # Getting file name from part number emboss + # name_options = [obj.String for obj in doc.Objects if + # obj.isDerivedFrom("Part::Part2DObject") and obj.Label == "PN"] + # if name_options: + # name = name_options[0] + # else: + # # If there is no part number embossed throw error + # raise ValueError("Part " + cad_file.name + " doesn't have a ShapeString called PN for part number emboss") + + # if cad_file.name[:8] != name[:8]: + # # STL model file name does not match the part number embedded in the file + # raise ValueError( + # "Part " + cad_file.name[:8] + " doesn't match the part number in the FreeCad model - " + name[:8]) + + body = [obj for obj in doc.Objects if obj.Label == "Body"] + + if len(body) == 0: + print(f"Cannot find body object. {len(doc.Objects)} objects present") + for obj in doc.Objects: + print(f"- {obj.Label}") + + raise Exception(f"Object named 'Body' not found in model {cad_file.name}") + + body = body[0] + + # Find font references in the model and ensure they point to the correct font file + fonts = [obj for obj in doc.Objects if + obj.isDerivedFrom("Part::Part2DObject") and hasattr(obj, "FontFile")] + + for obj in fonts: + font_file_property = obj.getPropertyByName('FontFile') + new_font_file = os.path.join(font_folder, os.path.split(font_file_property)[1]) + if not os.path.isfile(new_font_file): + raise FileNotFoundError(f"Cannot find font file {new_font_file}") + + if new_font_file != font_file_property: + print(f"\tCorrected '{obj.Label}' font file name from {font_file_property}") + setattr(obj, "FontFile", new_font_file) + obj.touch() + + # Recompute the model to ensure its valid and does not contain broken references or edges + # Mark each object as "changed" + for obj in doc.Objects: + obj.touch() + + # Recompute the entire document + t0 = time.perf_counter() + doc.recompute(None, True, True) + t1 = time.perf_counter() + total = t1 - t0 + print(f"\tRecompute of model took {total:3f}s") + + # Now check for any invalid shapes + # for obj in doc.Objects: + # if 'Invalid' in obj.State: + # raise Exception(f"Shape '{obj.Name}' in model '{cad_file.name}' is invalid") + + shape = body.Shape.copy(False) + + print_planes = [obj for obj in doc.Objects if obj.Label == "PrintPlane"] + if print_planes: + plane = print_planes[0] + matrix = plane.Placement.Matrix.inverse() + matrix.rotateX(math.pi) + shape = shape.transformShape(matrix) + # Very useful debug info if shape orientation gets funky + # print(f"\t\tShape Placement: {shape.Placement}") + # print(f"\t\tPlane Placement: {plane.Placement}") + # print(f"\t\tPlane Offset: {plane.AttachmentOffset}") + # print(f"\t\tMin X: {round(min(v.Point.x for v in shape.Vertexes), 2)}") + # print(f"\t\tMax X: {round(max(v.Point.x for v in shape.Vertexes), 2)}") + # print(f"\t\tMin Y: {round(min(v.Point.y for v in shape.Vertexes), 2)}") + # print(f"\t\tMax Y: {round(max(v.Point.y for v in shape.Vertexes), 2)}") + # print(f"\t\tMin Z: {round(min(v.Point.z for v in shape.Vertexes), 2)}") + # print(f"\t\tMax Z: {round(max(v.Point.z for v in shape.Vertexes), 2)}") + else: + print(f"\tWarning, missing PrintPlane object in file {cad_file.name}") + + # Delete any STL files with similar names (to cater for increments in version number) + delete_files = Path('stl-export').glob(name[0:9] + '??.stl') + for to_delete in delete_files: + print(f"\tDeleting previous STL model {to_delete}") + os.remove(to_delete) + + # Generate STL + mesh = doc.addObject("Mesh::Feature", "Mesh") + mesh.Mesh = MeshPart.meshFromShape(Shape=shape, LinearDeflection=0.01, AngularDeflection=0.025, Relative=False) + mesh.Mesh.write("stl-export/" + name + ".stl") + FreeCAD.closeDocument(doc.Name) + print(f"\tGenerated file 3D-Prints/{name}.stl") + + +if __name__ == '__main__': + # Create output folder if needed + output_directory = Path('stl-export') + output_directory.mkdir(parents=True, exist_ok=True) + + fdm_path = Path('../../../pnp/cad/FDM') + + exceptions: List[Exception] = [] + + # Use command line supplied file list if we have one + files = [] + + for p in sys.argv[1:]: + # Strip any folder names from parameter and assume it's a file in FDM folder + files.append(fdm_path.joinpath(Path(Path(p).name))) + + # If no command line, scan the folder + if len(files) == 0: + files = sorted(fdm_path.glob('*.FCStd')) + + for f in files: + try: + process_file(f) + except Exception as ex: + print(f"****") + print(f"\tAn error occurred while processing {str(f)}:") + print(f"\t{ex}") + traceback.print_exc() + print(f"****") + exceptions.append(ex) + + if exceptions: + verb = "was" if len(exceptions) == 1 else "were" + noun = "exception" if len(exceptions) == 1 else "exceptions" + print(f"There {verb} {len(exceptions)} {noun}") + + assert len(exceptions) == 0 diff --git a/.github/workflows/scripts/generate-stl-img.py b/.github/workflows/scripts/generate-stl-img.py new file mode 100644 index 0000000..2cb014a --- /dev/null +++ b/.github/workflows/scripts/generate-stl-img.py @@ -0,0 +1,42 @@ +# +# Exports 3D STL files to a static image for documentation +# + +import glob +import subprocess +import os +import sys + +dirName = "Feeder-" + sys.argv[1] + "/img" +if not os.path.exists(dirName): + os.mkdir(dirName) + print("Directory " , dirName , " Created ") +else: + print("Directory " , dirName , " already exists") + +for name in glob.glob(".github/workflows/scripts/stl-export/*.stl"): + + print(name) + + file=os.path.abspath(name) + + f = open("./render_image.scad", "w") + f.write("import(\"") + f.write(file) + f.write("\", convexity=3);") + f.close() + + base = os.path.splitext(file)[0] + base = dirName + "/" + os.path.basename(name) + base = base[:-4] + print(base) + + head, sep, tail = base.partition('_') + base = head + + + subprocess.call(["openscad","-o",base+".png", "--quiet", "--render", "--projection=o", "--viewall","--colorscheme","BeforeDawn", "--imgsize", "512,512", "--hardwarnings", "./render_image.scad" ]) + #subprocess.call(["/Applications/OpenSCAD.app/Contents/MacOS/OpenSCAD","-o",base+".png", "--quiet", "--render", "--projection=o", "--viewall","--colorscheme","BeforeDawn", "--imgsize", "512,512", "--hardwarnings", "./render_image.scad" ]) + +if os.path.exists("./render_image.scad"): + os.remove("./render_image.scad") diff --git a/.github/workflows/scripts/kibot/config-2layer.kibot.yaml b/.github/workflows/scripts/kibot/config-2layer.kibot.yaml new file mode 100644 index 0000000..6c1275b --- /dev/null +++ b/.github/workflows/scripts/kibot/config-2layer.kibot.yaml @@ -0,0 +1,215 @@ +# Example KiPlot config file +kibot: + version: 1 + +filters: + - name: only_jlc_parts + comment: 'Only parts with JLC code' + type: 'generic' + include_only: + - column: 'JLCPCB' + regex: '^C\d+' + - name: fix_rotation + comment: 'Adjust rotation for JLC' + type: rot_footprint + rotations: + - ["^TI_SO-", 270.0] + - ["^SO-", 270.0] + - ["^HTSSOP-", 270.0] + - ["^SSOP-", 270.0] + +global: + variant: default + +variants: + - name: default + comment: 'Just a place holder for the rotation filter' + type: kibom + variant: default + - name: jlcpcb + comment: 'JLCPCB requires some rotational transforms' + type: kibom + variant: jlcpcb + pre_transform: fix_rotation + +preflight: + # Disable ERC for now while GPereira updates these items. + run_erc: false + update_xml: true + # Disable DRC for now while GPereira updates these items. + run_drc: false + check_zone_fills: true + ignore_unconnected: false + +outputs: + - name: 'ibom' + comment: 'Interactive Bill of Materials' + type: ibom + dir: ibom + + - name: 'bom' + comment: 'Bill of Materials' + type: bom + dir: . + options: + csv: + hide_pcb_info: true + hide_stats_info: true + format: CSV + + - name: 'html_bom' + comment: 'HTML BOM' + type: bom + dir: . + options: + html: + datasheet_as_link: Datasheet + digikey_link: Digikey + title: 'Index MOBO Bill of Materials' + + - name: 'print_sch' + comment: "Print schematic (PDF)" + type: pdf_sch_print + dir: . + options: + output: Schematic.pdf + +#removing to prevent ci from hanging while exporting pdf +# - name: 'print_front' +# comment: "Print F.Cu+Dwgs.User" +# type: pdf_pcb_print +# dir: . +# options: +# output_name: PCB.pdf +# separated: true +# layers: +# - layer: F.Cu +# description: 'Front Copper' +# - layer: B.Cu +# description: 'Back Copper' +# - layer: F.SilkS +# description: 'Front Silk' +# - layer: B.SilkS +# description: 'Rear Silk' + + - name: 'gerbers' + comment: "Gerbers for the board house" + type: gerber + dir: gerbers + options: + # generic layer options + exclude_edge_layer: true + exclude_pads_from_silkscreen: false + use_aux_axis_as_origin: false + plot_sheet_reference: false + plot_footprint_refs: true + plot_footprint_values: true + force_plot_invisible_refs_vals: false + tent_vias: true + + # gerber options + line_width: 0.1 + subtract_mask_from_silk: false + use_protel_extensions: false + gerber_precision: 4.6 + create_gerber_job_file: true + use_gerber_x2_attributes: false + use_gerber_net_attributes: false + + output: '%f.%i' + + + layers: + # When Moving to Four Layer, Set G2L and G3L as the suffixes + - layer: F.Cu + suffix: GTL + - layer: B.Cu + suffix: GBL + - layer: F.SilkS + suffix: GTO + - layer: B.SilkS + suffix: GBO + - layer: F.Mask + suffix: GTS + - layer: B.Mask + suffix: GBS + - layer: Edge.Cuts + suffix: GKO + + - name: 'drill_file' + comment: 'Drill file for Board House' + type: excellon + dir: gerbers + options: + metric_units: false + pth_and_npth_single_file: true + + - name: board_top + comment: "Top layer view" + type: pcbdraw + dir: . + options: + format: png + + - name: board_bottom + comment: "Bottom layer view" + type: pcbdraw + dir: . + options: + format: png + bottom: true + + - name: 'pick_and_place_file' + comment: 'Pick and Place Location File' + type: position + dir: gerbers + options: + format: CSV + + - name: 'bom_jlc' + comment: "BoM for JLC" + type: bom + options: + output: '%f_bom_jlc.%x' + # exclude_filter: only_jlc_parts + ref_separator: ',' + columns: + - field: Value + name: Comment + - field: References + name: Designator + - field: Footprint + name: Footprint + - field: JLCPCB + name: 'LCSC Part #' + csv: + hide_pcb_info: true + hide_stats_info: true + quote_all: true + + - name: 'pick_and_place_jlc' + comment: 'Pick and place file, JLC style' + type: position + options: + output: '%f_cpl_jlc.%x' + format: CSV + units: millimeters + separate_files_for_front_and_back: false + only_smd: true + variant: jlcpcb + columns: + - id: Ref + name: Designator + - id: PosX + name: "Mid X" + - id: PosY + name: "Mid Y" + - id: Side + name: Layer + - id: Rot + name: Rotation + + - name: 'step_file' + comment: 'STEP file generation' + type: step + dir: . diff --git a/.github/workflows/scripts/kibot/config-4layer.kibot.yaml b/.github/workflows/scripts/kibot/config-4layer.kibot.yaml new file mode 100644 index 0000000..99bb430 --- /dev/null +++ b/.github/workflows/scripts/kibot/config-4layer.kibot.yaml @@ -0,0 +1,247 @@ +# Example KiPlot config file +kibot: + version: 1 + +filters: + - name: only_jlc_parts + comment: 'Only parts with JLC code' + type: 'generic' + include_only: + - column: 'JLCPCB' + regex: '^C\d+' + - name: fix_rotation + comment: 'Adjust rotation for JLC' + type: rot_footprint + rotations: + - ["^TI_SO-", 270.0] + - ["^SO-", 270.0] + - ["^HTSSOP-", 270.0] + - ["^SSOP-", 270.0] + - ["^MSOP-", 270.0] + - ["^USB_C_Receptacle_Palconn", 180.0] + +global: + variant: default + +variants: + - name: default + comment: 'Just a place holder for the rotation filter' + type: kibom + variant: default + - name: jlcpcb + comment: 'JLCPCB requires some rotational transforms' + type: kibom + variant: jlcpcb + pre_transform: fix_rotation + +preflight: + run_erc: true + update_xml: true + # Disable DRC for now while GPereira updates these items. + run_drc: false + check_zone_fills: true + ignore_unconnected: false + +outputs: + - name: 'ibom' + comment: 'Interactive Bill of Materials' + type: ibom + dir: ibom + + - name: 'bom' + comment: 'Bill of Materials' + type: bom + dir: . + options: + csv: + hide_pcb_info: true + hide_stats_info: true + format: CSV + + - name: 'html_bom' + comment: 'HTML BOM' + type: bom + dir: . + options: + html: + datasheet_as_link: Datasheet + digikey_link: Digikey + title: 'Index MOBO Bill of Materials' + + - name: 'print_sch' + comment: "Print schematic (PDF)" + type: pdf_sch_print + dir: . + options: + output: Schematic.pdf + +# removing to prevent ci from hanging from long export time + +# - name: 'print_front' +# comment: "Print F.Cu+Dwgs.User" +# type: pdf_pcb_print +# dir: . +# options: +# output_name: PCB.pdf +# separated: true +# layers: +# - layer: F.Cu +# description: 'Front Copper' +# - layer: In1.Cu +# description: 'Layer 2' +# - layer: In2.Cu +# description: 'Layer 3' +# - layer: B.Cu +# description: 'Back Copper' +# - layer: F.SilkS +# description: 'Front Silk' +# - layer: B.SilkS +# description: 'Rear Silk' + + - name: 'gerbers' + comment: "Gerbers for the board house" + type: gerber + dir: gerbers + options: + # generic layer options + exclude_edge_layer: true + exclude_pads_from_silkscreen: false + use_aux_axis_as_origin: false + plot_sheet_reference: false + plot_footprint_refs: true + plot_footprint_values: true + force_plot_invisible_refs_vals: false + tent_vias: true + + # gerber options + line_width: 0.1 + subtract_mask_from_silk: false + use_protel_extensions: false + gerber_precision: 4.6 + create_gerber_job_file: true + use_gerber_x2_attributes: false + use_gerber_net_attributes: false + + output: '%f.%i' + + + layers: + # When Moving to Four Layer, Set G2L and G3L as the suffixes + - layer: F.Cu + suffix: GTL + - layer: In1.Cu + suffix: G2L + - layer: In2.Cu + suffix: G3L + - layer: B.Cu + suffix: GBL + - layer: F.SilkS + suffix: GTO + - layer: B.SilkS + suffix: GBO + - layer: F.Mask + suffix: GTS + - layer: B.Mask + suffix: GBS + - layer: Edge.Cuts + suffix: GKO + + - name: 'drill_file' + comment: 'Drill file for Board House' + type: excellon + dir: gerbers + options: + metric_units: false + pth_and_npth_single_file: true + + - name: board_top + comment: "Top layer view" + type: pcbdraw + dir: . + options: + format: png + + - name: board_bottom + comment: "Bottom layer view" + type: pcbdraw + dir: . + options: + format: png + bottom: true + + - name: 'pick_and_place_file' + comment: 'Pick and Place Location File' + type: position + dir: gerbers + options: + format: CSV + + - name: 'bom_jlc' + comment: "BoM for JLC" + type: bom + options: + output: '%f_bom_jlc.%x' + # exclude_filter: only_jlc_parts + ref_separator: ',' + columns: + - field: Value + name: Comment + - field: References + name: Designator + - field: Footprint + name: Footprint + - field: JLCPCB + name: 'LCSC Part #' + csv: + hide_pcb_info: true + hide_stats_info: true + quote_all: true + + - name: 'pick_and_place_jlc' + comment: 'Pick and place file, JLC style' + type: position + options: + output: '%f_cpl_jlc.%x' + format: CSV + units: millimeters + separate_files_for_front_and_back: false + only_smd: true + variant: jlcpcb + columns: + - id: Ref + name: Designator + - id: PosX + name: "Mid X" + - id: PosY + name: "Mid Y" + - id: Side + name: Layer + - id: Rot + name: Rotation + + - name: 'pick_and_place_jlc_with_connectors' + comment: 'Pick and place file, JLC style with connectors' + type: position + options: + output: '%f_cpl_jlc_conn.%x' + format: CSV + units: millimeters + separate_files_for_front_and_back: false + only_smd: false + variant: jlcpcb + columns: + - id: Ref + name: Designator + - id: PosX + name: "Mid X" + - id: PosY + name: "Mid Y" + - id: Side + name: Layer + - id: Rot + name: Rotation + + - name: 'step_file' + comment: 'STEP file generation' + type: step + dir: . diff --git a/.github/workflows/scripts/kibot/config-blank.kibot.yaml b/.github/workflows/scripts/kibot/config-blank.kibot.yaml new file mode 100644 index 0000000..46f949a --- /dev/null +++ b/.github/workflows/scripts/kibot/config-blank.kibot.yaml @@ -0,0 +1,95 @@ +# Example KiPlot config file +kibot: + version: 1 + +global: + variant: default + +variants: + - name: default + comment: 'Just a place holder for the rotation filter' + type: kibom + variant: default + +preflight: + # Disable ERC for now while GPereira updates these items. + run_erc: false + update_xml: true + # Disable DRC for now while GPereira updates these items. + run_drc: false + check_zone_fills: true + ignore_unconnected: false + +outputs: + + - name: 'gerbers' + comment: "Gerbers for the board house" + type: gerber + dir: gerbers + options: + # generic layer options + exclude_edge_layer: true + exclude_pads_from_silkscreen: false + use_aux_axis_as_origin: false + plot_sheet_reference: false + plot_footprint_refs: true + plot_footprint_values: true + force_plot_invisible_refs_vals: false + tent_vias: true + + # gerber options + line_width: 0.1 + subtract_mask_from_silk: false + use_protel_extensions: false + gerber_precision: 4.6 + create_gerber_job_file: true + use_gerber_x2_attributes: false + use_gerber_net_attributes: false + + output: '%f.%i' + + + layers: + # When Moving to Four Layer, Set G2L and G3L as the suffixes + - layer: F.Cu + suffix: GTL + - layer: B.Cu + suffix: GBL + - layer: F.SilkS + suffix: GTO + - layer: B.SilkS + suffix: GBO + - layer: F.Mask + suffix: GTS + - layer: B.Mask + suffix: GBS + - layer: Edge.Cuts + suffix: GKO + + - name: 'drill_file' + comment: 'Drill file for Board House' + type: excellon + dir: gerbers + options: + metric_units: false + pth_and_npth_single_file: true + + - name: board_top + comment: "Top layer view" + type: pcbdraw + dir: . + options: + format: png + + - name: board_bottom + comment: "Bottom layer view" + type: pcbdraw + dir: . + options: + format: png + bottom: true + + - name: 'step_file' + comment: 'STEP file generation' + type: step + dir: . diff --git a/.github/workflows/scripts/kibot/config.kibot.yaml b/.github/workflows/scripts/kibot/config.kibot.yaml new file mode 100644 index 0000000..1053b61 --- /dev/null +++ b/.github/workflows/scripts/kibot/config.kibot.yaml @@ -0,0 +1,229 @@ +# Example KiPlot config file +kibot: + version: 1 + +filters: + - name: only_jlc_parts + comment: 'Only parts with JLC code' + type: 'generic' + include_only: + - column: 'JLCPCB' + regex: '^C\d+' + - name: fix_rotation + comment: 'Adjust rotation for JLC' + type: rot_footprint + rotations: + - ["^TI_SO-", 270.0] + - ["^SO-", 270.0] + - ["^HTSSOP-", 270.0] + +global: + variant: default + +variants: + - name: default + comment: 'Just a place holder for the rotation filter' + type: kibom + variant: default + - name: jlcpcb + comment: 'JLCPCB requires some rotational transforms' + type: kibom + variant: jlcpcb + pre_transform: fix_rotation + +preflight: + # Disable ERC for now while GPereira updates these items. + run_erc: false + update_xml: true + # Disable DRC for now while GPereira updates these items. + run_drc: false + check_zone_fills: true + ignore_unconnected: false + +outputs: + - name: 'ibom' + comment: 'Interactive Bill of Materials' + type: ibom + dir: ibom + + - name: 'bom' + comment: 'Bill of Materials' + type: bom + dir: . + options: + csv: + hide_pcb_info: true + hide_stats_info: true + format: CSV + + - name: 'html_bom' + comment: 'HTML BOM' + type: bom + dir: . + options: + html: + datasheet_as_link: Datasheet + digikey_link: Digikey + title: 'Index MOBO Bill of Materials' + + - name: 'print_sch' + comment: "Print schematic (PDF)" + type: pdf_sch_print + dir: . + options: + output: Schematic.pdf + + - name: 'print_front' + comment: "Print F.Cu+Dwgs.User" + type: pdf_pcb_print + dir: . + options: + output_name: PCB.pdf + separated: true + layers: + - layer: F.Cu + description: 'Front Copper' + - layer: B.Cu + description: 'Back Copper' + - layer: F.SilkS + description: 'Front Silk' + - layer: B.SilkS + description: 'Rear Silk' + + - name: 'gerbers' + comment: "Gerbers for the board house" + type: gerber + dir: gerbers + options: + # generic layer options + exclude_edge_layer: true + exclude_pads_from_silkscreen: false + use_aux_axis_as_origin: false + plot_sheet_reference: false + plot_footprint_refs: true + plot_footprint_values: true + force_plot_invisible_refs_vals: false + tent_vias: true + + # gerber options + line_width: 0.1 + subtract_mask_from_silk: false + use_protel_extensions: false + gerber_precision: 4.6 + create_gerber_job_file: true + use_gerber_x2_attributes: false + use_gerber_net_attributes: false + + output: '%f.%i' + + + layers: + # When Moving to Four Layer, Set G2L and G3L as the suffixes + - layer: F.Cu + suffix: GTL + - layer: B.Cu + suffix: GBL + - layer: F.SilkS + suffix: GTO + - layer: B.SilkS + suffix: GBO + - layer: F.Mask + suffix: GTS + - layer: B.Mask + suffix: GBS + - layer: Edge.Cuts + suffix: GKO + - layer: F.Paste + suffix: GTP + - layer: B.Paste + suffix: GBP + + - name: 'drill_file' + comment: 'Drill file for Board House' + type: excellon + dir: gerbers + options: + metric_units: false + pth_and_npth_single_file: true + + - name: board_top + comment: "Top layer view" + type: pcbdraw + dir: . + options: + format: png + show_components: all + style: + board: '#242424' + copper: '#404040' + silk: '#ffffff' + pads: '#bfbfbf' + + - name: board_bottom + comment: "Bottom layer view" + type: pcbdraw + dir: . + options: + format: png + bottom: true + show_components: all + style: + board: '#242424' + copper: '#404040' + silk: '#ffffff' + pads: '#bfbfbf' + + - name: 'pick_and_place_file' + comment: 'Pick and Place Location File' + type: position + dir: gerbers + options: + format: CSV + + - name: 'bom_jlc' + comment: "BoM for JLC" + type: bom + options: + output: '%f_bom_jlc.%x' + # exclude_filter: only_jlc_parts + ref_separator: ',' + columns: + - field: Value + name: Comment + - field: References + name: Designator + - field: Footprint + name: Footprint + - field: JLCPCB + name: 'LCSC Part #' + csv: + hide_pcb_info: true + hide_stats_info: true + quote_all: true + + - name: 'pick_and_place_jlc' + comment: 'Pick and place file, JLC style' + type: position + options: + output: '%f_cpl_jlc.%x' + format: CSV + units: millimeters + separate_files_for_front_and_back: false + only_smd: true + variant: jlcpcb + columns: + - id: Ref + name: Designator + - id: PosX + name: "Mid X" + - id: PosY + name: "Mid Y" + - id: Side + name: Layer + - id: Rot + name: Rotation + + - name: 'step_file' + comment: 'STEP file generation' + type: step + dir: .