diff --git a/.github/workflows/openscad-render.yml b/.github/workflows/openscad-render.yml index c3dbefb..a65d982 100644 --- a/.github/workflows/openscad-render.yml +++ b/.github/workflows/openscad-render.yml @@ -11,9 +11,9 @@ on: workflow_dispatch: jobs: - validate-and-render: + render: runs-on: ubuntu-latest - name: Validate OpenSCAD and Generate Outputs + name: Generate Renders and Outputs steps: - name: Checkout repository @@ -22,35 +22,31 @@ jobs: - name: Install OpenSCAD run: | sudo apt-get update - sudo apt-get install -y openscad xvfb + sudo apt-get install -y openscad xvfb x11-utils openscad --version - - name: Validate OpenSCAD syntax + - name: Start Xvfb run: | - echo "Validating OpenSCAD files..." - cd LifeTrac-v25/mechanical_design + Xvfb :99 -screen 0 1024x768x16 & + XVFB_PID=$! + echo "XVFB_PID=$XVFB_PID" >> $GITHUB_ENV - # Check main design file - xvfb-run openscad -o /dev/null --export-format echo openscad/lifetrac_v25.scad - if [ $? -eq 0 ]; then - echo "✓ Main design file is valid" - else - echo "✗ Main design file has errors" - exit 1 - fi - - # Check module files - for module in modules/*.scad; do - echo "Checking $module..." - xvfb-run openscad -o /dev/null --export-format echo "$module" - if [ $? -eq 0 ]; then - echo "✓ $module is valid" - else - echo "✗ $module has errors" + # Wait for Xvfb to be ready (max 10 seconds) + for i in {1..10}; do + if xdpyinfo -display :99 >/dev/null 2>&1; then + echo "Xvfb is ready on display :99" + break + fi + if [ $i -eq 10 ]; then + echo "Timeout waiting for Xvfb to start" exit 1 fi + sleep 1 done + + echo "DISPLAY=:99" >> $GITHUB_ENV + - name: Create output directories run: | cd LifeTrac-v25/mechanical_design @@ -62,7 +58,7 @@ jobs: # Generate assembly.png (1024x768 matching CEB-Press example) echo "Generating assembly.png..." - xvfb-run openscad -o assembly.png \ + openscad -o assembly.png \ --camera=3000,3000,2000,0,0,500 \ --imgsize=1024,768 \ --projection=p \ @@ -71,7 +67,7 @@ jobs: # Generate cnclayout.svg (2D projection for CNC cutting) echo "Generating cnclayout.svg..." - xvfb-run openscad -o cnclayout.svg \ + openscad -o cnclayout.svg \ --projection=o \ --viewall \ cnclayout.scad @@ -85,7 +81,7 @@ jobs: # Main assembly view echo "Rendering main assembly..." - xvfb-run openscad -o output/renders/lifetrac_v25_main.png \ + openscad -o output/renders/lifetrac_v25_main.png \ --camera=3000,3000,2000,0,0,500 \ --imgsize=1920,1080 \ --projection=p \ @@ -94,7 +90,7 @@ jobs: # Front view echo "Rendering front view..." - xvfb-run openscad -o output/renders/lifetrac_v25_front.png \ + openscad -o output/renders/lifetrac_v25_front.png \ --camera=0,3000,1000,0,0,500 \ --imgsize=1920,1080 \ --projection=p \ @@ -103,7 +99,7 @@ jobs: # Side view echo "Rendering side view..." - xvfb-run openscad -o output/renders/lifetrac_v25_side.png \ + openscad -o output/renders/lifetrac_v25_side.png \ --camera=3000,0,1000,0,0,500 \ --imgsize=1920,1080 \ --projection=p \ @@ -112,7 +108,7 @@ jobs: # Top view echo "Rendering top view..." - xvfb-run openscad -o output/renders/lifetrac_v25_top.png \ + openscad -o output/renders/lifetrac_v25_top.png \ --camera=0,0,3000,0,0,500 \ --imgsize=1920,1080 \ --projection=o \ @@ -129,7 +125,7 @@ jobs: for module in modules/*.scad; do filename=$(basename "$module" .scad) echo "Rendering $filename examples..." - xvfb-run openscad -o "output/renders/${filename}_examples.png" \ + openscad -o "output/renders/${filename}_examples.png" \ --camera=500,500,500,0,0,0 \ --imgsize=1600,900 \ --projection=p \ @@ -148,7 +144,7 @@ jobs: for i in {0..35}; do t=$(awk "BEGIN {print $i/36}") printf "Frame %d (t=%.4f)\n" $i $t - xvfb-run openscad -o "output/animations/frames/frame_$(printf %03d $i).png" \ + openscad -o "output/animations/frames/frame_$(printf %03d $i).png" \ --camera=3000,3000,2000,0,0,500 \ --imgsize=1280,720 \ --projection=p \ @@ -159,19 +155,19 @@ jobs: echo "Animation frames generated" - - name: Create animation GIF (if imagemagick available) - continue-on-error: true + - name: Create animation GIF run: | sudo apt-get install -y imagemagick cd LifeTrac-v25/mechanical_design/output/animations - if command -v convert &> /dev/null; then - echo "Creating animation GIF..." - convert -delay 10 -loop 0 frames/frame_*.png lifetrac_v25_animation.gif - echo "Animation GIF created" - else - echo "ImageMagick not available, skipping GIF creation" - fi + echo "Creating animation GIF of arm movement..." + convert -delay 10 -loop 0 frames/frame_*.png lifetrac_v25_animation.gif + + # Copy to parent directory for easy access + cp lifetrac_v25_animation.gif ../../lifetrac_v25_animation.gif + + echo "✓ Animation GIF created successfully" + ls -lh ../../lifetrac_v25_animation.gif - name: Generate summary report run: | @@ -194,25 +190,21 @@ jobs: - Module examples (5 modules) ### Animations - - 36 animation frames - - Animation GIF (if ImageMagick available) + - 36 animation frames (1280x720 PNG) + - Animated GIF showing arm movement (lifetrac_v25_animation.gif) ### CNC Files - assembly.png (3D render of complete assembly) - cnclayout.svg (2D layout for CNC cutting) - ## Validation Results - - ✓ All OpenSCAD files validated successfully - ✓ Main design file compiled without errors - ✓ All module files syntax-checked - ## Next Steps 1. Review rendered images in the artifacts - 2. Use cnclayout.svg for CNC plasma cutting - 3. Use animation frames to create videos - 4. Export DXF files for individual parts using export_all_cnc_parts.sh + 2. View lifetrac_v25_animation.gif to see arm movement + 3. Use cnclayout.svg for CNC plasma cutting + 4. Use animation frames to create videos + 5. Export DXF files for individual parts using export_all_cnc_parts.sh + 6. Check the Structural Analysis workflow for design validation results --- Generated by GitHub Actions @@ -234,28 +226,48 @@ jobs: path: LifeTrac-v25/mechanical_design/output/animations/ retention-days: 90 - - name: Commit and push assembly.png and cnclayout.svg + - name: Commit and push rendered outputs run: | cd LifeTrac-v25/mechanical_design git config --global user.name 'github-actions[bot]' git config --global user.email 'github-actions[bot]@users.noreply.github.com' # Add the generated files - git add assembly.png cnclayout.svg + git add assembly.png cnclayout.svg lifetrac_v25_animation.gif # Check if there are changes if ! git diff --staged --quiet; then - git commit -m "Update assembly.png and cnclayout.svg [skip ci]" + git commit -m "Update rendered outputs (assembly, layout, animation) [skip ci]" git push --force-with-lease origin HEAD || { echo "INFO: git push failed. This is expected on PRs from forks." echo "The files are available as workflow artifacts." } else - echo "No changes to assembly.png or cnclayout.svg" + echo "No changes to rendered output files" fi env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Cleanup Xvfb + if: always() + run: | + if [ -n "$XVFB_PID" ]; then + echo "Stopping Xvfb process (PID: $XVFB_PID)" + # Try graceful shutdown first + kill -TERM $XVFB_PID 2>/dev/null || true + # Wait up to 5 seconds for process to terminate + for i in {1..5}; do + if ! kill -0 $XVFB_PID 2>/dev/null; then + echo "Xvfb stopped gracefully" + break + fi + sleep 1 + done + # Force kill if still running + kill -KILL $XVFB_PID 2>/dev/null || true + echo "Xvfb force stopped" + fi + - name: Comment on PR with results if: github.event_name == 'pull_request' uses: actions/github-script@v7 @@ -264,11 +276,12 @@ jobs: const fs = require('fs'); const comment = `## OpenSCAD Render Results 🎨 - ✅ All OpenSCAD files validated successfully! + ✅ Rendering completed successfully! ### Generated Outputs - 📸 Preview renders (main, front, side, top views) - 🎬 Animation frames (36 frames) + - 🎞️ Animated GIF of arm movement (lifetrac_v25_animation.gif) - 🔧 Module examples - 🖼️ Assembly image and CNC layout (assembly.png, cnclayout.svg) @@ -277,8 +290,13 @@ jobs: - \`openscad-renders\` - Preview images - \`openscad-animations\` - Animation frames and GIF - ### CNC Files - - \`assembly.png\` and \`cnclayout.svg\` committed to repository + ### Committed Files + The following files have been committed to the repository: + - \`assembly.png\` - 3D assembly render + - \`cnclayout.svg\` - 2D CNC cutting layout + - \`lifetrac_v25_animation.gif\` - Animation showing arm movement + + _Note: Check the **OpenSCAD Structural Analysis** workflow for design validation and structural analysis results._ **Workflow Run:** [#${{ github.run_number }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) `; diff --git a/.github/workflows/openscad-structural-analysis.yml b/.github/workflows/openscad-structural-analysis.yml new file mode 100644 index 0000000..603a25c --- /dev/null +++ b/.github/workflows/openscad-structural-analysis.yml @@ -0,0 +1,300 @@ +name: OpenSCAD Structural Analysis + +on: + push: + paths: + - 'LifeTrac-v25/mechanical_design/**/*.scad' + - '.github/workflows/openscad-structural-analysis.yml' + pull_request: + paths: + - 'LifeTrac-v25/mechanical_design/**/*.scad' + workflow_dispatch: + +jobs: + structural-analysis: + runs-on: ubuntu-latest + name: Validate Structural Design + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install OpenSCAD + run: | + sudo apt-get update + sudo apt-get install -y openscad xvfb x11-utils + openscad --version + + - name: Start Xvfb + run: | + Xvfb :99 -screen 0 1024x768x16 & + XVFB_PID=$! + echo "XVFB_PID=$XVFB_PID" >> $GITHUB_ENV + + # Wait for Xvfb to be ready (max 10 seconds) + for i in {1..10}; do + if xdpyinfo -display :99 >/dev/null 2>&1; then + echo "Xvfb is ready on display :99" + break + fi + if [ $i -eq 10 ]; then + echo "Timeout waiting for Xvfb to start" + exit 1 + fi + sleep 1 + done + + echo "DISPLAY=:99" >> $GITHUB_ENV + + - name: Validate OpenSCAD syntax + run: | + echo "Validating OpenSCAD files..." + cd LifeTrac-v25/mechanical_design + + # Check main design file + openscad -o /dev/null --export-format echo openscad/lifetrac_v25.scad + if [ $? -eq 0 ]; then + echo "✓ Main design file is valid" + else + echo "✗ Main design file has errors" + exit 1 + fi + + # Check module files + for module in modules/*.scad; do + echo "Checking $module..." + openscad -o /dev/null --export-format echo "$module" + if [ $? -eq 0 ]; then + echo "✓ $module is valid" + else + echo "✗ $module has errors" + exit 1 + fi + done + + - name: Run structural analysis + run: | + echo "Running structural analysis on main design..." + cd LifeTrac-v25/mechanical_design + + # Run OpenSCAD with echo output to capture structural analysis + openscad -o /dev/null --export-format echo openscad/lifetrac_v25.scad 2>&1 | tee structural_analysis.log + + echo "Structural analysis complete. Check logs for results." + + - name: Generate structural analysis report + run: | + cd LifeTrac-v25/mechanical_design + + # Extract key metrics from the log + echo "# Structural Analysis Report" > structural_report.md + echo "" >> structural_report.md + echo "**Generated:** $(date)" >> structural_report.md + echo "**Commit:** ${{ github.sha }}" >> structural_report.md + echo "**Branch:** ${{ github.ref_name }}" >> structural_report.md + echo "" >> structural_report.md + + # Extract structural summary if present + if grep -q "COMPLETE STRUCTURAL ANALYSIS SUMMARY" structural_analysis.log; then + echo "## Structural Analysis Results" >> structural_report.md + echo "" >> structural_report.md + echo "\`\`\`" >> structural_report.md + # Extract from summary start to next delimiter or end of relevant section + sed -n '/COMPLETE STRUCTURAL ANALYSIS SUMMARY/,/========================================/{p;/========================================/q}' structural_analysis.log | tail -n +2 >> structural_report.md || \ + sed -n '/COMPLETE STRUCTURAL ANALYSIS SUMMARY/,$p' structural_analysis.log | tail -n +2 | head -n 20 >> structural_report.md + echo "\`\`\`" >> structural_report.md + else + echo "No structural analysis summary found in output." >> structural_report.md + fi + + cat structural_report.md + + - name: Create persistent structural analysis log + run: | + cd LifeTrac-v25/mechanical_design + + # Create or update the persistent log file + echo "# Structural Analysis Test Log" > STRUCTURAL_ANALYSIS_LOG.md + echo "" >> STRUCTURAL_ANALYSIS_LOG.md + echo "This file tracks the history of structural analysis test results." >> STRUCTURAL_ANALYSIS_LOG.md + echo "" >> STRUCTURAL_ANALYSIS_LOG.md + echo "---" >> STRUCTURAL_ANALYSIS_LOG.md + echo "" >> STRUCTURAL_ANALYSIS_LOG.md + echo "## Latest Analysis" >> STRUCTURAL_ANALYSIS_LOG.md + echo "" >> STRUCTURAL_ANALYSIS_LOG.md + echo "**Date:** $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> STRUCTURAL_ANALYSIS_LOG.md + echo "**Commit:** ${{ github.sha }}" >> STRUCTURAL_ANALYSIS_LOG.md + echo "**Branch:** ${{ github.ref_name }}" >> STRUCTURAL_ANALYSIS_LOG.md + echo "**Workflow Run:** ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> STRUCTURAL_ANALYSIS_LOG.md + echo "" >> STRUCTURAL_ANALYSIS_LOG.md + + # Extract and format the structural analysis results + if grep -q "COMPLETE STRUCTURAL ANALYSIS SUMMARY" structural_analysis.log; then + echo "### Results Summary" >> STRUCTURAL_ANALYSIS_LOG.md + echo "" >> STRUCTURAL_ANALYSIS_LOG.md + echo "\`\`\`" >> STRUCTURAL_ANALYSIS_LOG.md + sed -n '/COMPLETE STRUCTURAL ANALYSIS SUMMARY/,/========================================/{p;/========================================/q}' structural_analysis.log | tail -n +2 >> STRUCTURAL_ANALYSIS_LOG.md || \ + sed -n '/COMPLETE STRUCTURAL ANALYSIS SUMMARY/,$p' structural_analysis.log | tail -n +2 | head -n 20 >> STRUCTURAL_ANALYSIS_LOG.md + echo "\`\`\`" >> STRUCTURAL_ANALYSIS_LOG.md + echo "" >> STRUCTURAL_ANALYSIS_LOG.md + + # Extract detailed failure information if any tests failed + if grep -q "FAIL" structural_analysis.log; then + echo "### ⚠️ Failed Components" >> STRUCTURAL_ANALYSIS_LOG.md + echo "" >> STRUCTURAL_ANALYSIS_LOG.md + + # Extract rated capacity + CAPACITY=$(grep "RATED LIFT CAPACITY:" structural_analysis.log | head -1 | sed 's/ECHO: "RATED LIFT CAPACITY:", //') + echo "**Current Rated Capacity:** $CAPACITY" >> STRUCTURAL_ANALYSIS_LOG.md + echo "" >> STRUCTURAL_ANALYSIS_LOG.md + + # List each failed component with details + echo "#### Detailed Failure Analysis" >> STRUCTURAL_ANALYSIS_LOG.md + echo "" >> STRUCTURAL_ANALYSIS_LOG.md + + # Arm Bending + if grep -q "Arm Bending.*FAIL" structural_analysis.log; then + echo "**1. Arm Bending Stress**" >> STRUCTURAL_ANALYSIS_LOG.md + grep -A 5 "=== ARM BENDING STRESS ANALYSIS ===" structural_analysis.log | grep "ECHO:" | sed 's/ECHO: /- /' >> STRUCTURAL_ANALYSIS_LOG.md + echo "" >> STRUCTURAL_ANALYSIS_LOG.md + fi + + # Arm Deflection + if grep -q "Arm Deflection.*FAIL" structural_analysis.log; then + echo "**2. Arm Deflection**" >> STRUCTURAL_ANALYSIS_LOG.md + grep -A 5 "=== ARM DEFLECTION ANALYSIS ===" structural_analysis.log | grep "ECHO:" | sed 's/ECHO: /- /' >> STRUCTURAL_ANALYSIS_LOG.md + echo "" >> STRUCTURAL_ANALYSIS_LOG.md + fi + + # Cross Beam + if grep -q "Cross Beam.*FAIL" structural_analysis.log; then + echo "**3. Cross Beam Bending**" >> STRUCTURAL_ANALYSIS_LOG.md + grep -A 5 "=== CROSS BEAM STRESS ANALYSIS ===" structural_analysis.log | grep "ECHO:" | sed 's/ECHO: /- /' >> STRUCTURAL_ANALYSIS_LOG.md + echo "" >> STRUCTURAL_ANALYSIS_LOG.md + fi + + # Pivot Ring Welds + if grep -q "Pivot Ring.*FAIL" structural_analysis.log; then + echo "**4. Pivot Ring Welds**" >> STRUCTURAL_ANALYSIS_LOG.md + grep -A 5 "=== PIVOT RING WELD ANALYSIS ===" structural_analysis.log | grep "ECHO:" | sed 's/ECHO: /- /' >> STRUCTURAL_ANALYSIS_LOG.md + echo "" >> STRUCTURAL_ANALYSIS_LOG.md + fi + + echo "### Recommended Actions" >> STRUCTURAL_ANALYSIS_LOG.md + echo "" >> STRUCTURAL_ANALYSIS_LOG.md + echo "See [STRUCTURAL_ANALYSIS.md](STRUCTURAL_ANALYSIS.md) for design modification recommendations." >> STRUCTURAL_ANALYSIS_LOG.md + echo "" >> STRUCTURAL_ANALYSIS_LOG.md + else + echo "### ✅ All Tests Passed" >> STRUCTURAL_ANALYSIS_LOG.md + echo "" >> STRUCTURAL_ANALYSIS_LOG.md + echo "All structural analysis checks passed successfully." >> STRUCTURAL_ANALYSIS_LOG.md + echo "" >> STRUCTURAL_ANALYSIS_LOG.md + fi + else + echo "### Error" >> STRUCTURAL_ANALYSIS_LOG.md + echo "" >> STRUCTURAL_ANALYSIS_LOG.md + echo "No structural analysis summary found in output. Check the workflow logs for errors." >> STRUCTURAL_ANALYSIS_LOG.md + echo "" >> STRUCTURAL_ANALYSIS_LOG.md + fi + + echo "---" >> STRUCTURAL_ANALYSIS_LOG.md + echo "" >> STRUCTURAL_ANALYSIS_LOG.md + echo "_This file is automatically updated by the OpenSCAD Structural Analysis workflow._" >> STRUCTURAL_ANALYSIS_LOG.md + + cat STRUCTURAL_ANALYSIS_LOG.md + + - name: Commit structural analysis log + run: | + cd LifeTrac-v25/mechanical_design + git config --global user.name 'github-actions[bot]' + git config --global user.email 'github-actions[bot]@users.noreply.github.com' + + # Add the log file + git add STRUCTURAL_ANALYSIS_LOG.md + + # Check if there are changes + if ! git diff --staged --quiet; then + git commit -m "Update structural analysis log [skip ci]" + git push --force-with-lease origin HEAD || { + echo "INFO: git push failed. This is expected on PRs from forks." + echo "The log file is available as a workflow artifact." + } + else + echo "No changes to structural analysis log" + fi + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload structural analysis artifacts + uses: actions/upload-artifact@v4 + with: + name: structural-analysis + path: | + LifeTrac-v25/mechanical_design/structural_analysis.log + LifeTrac-v25/mechanical_design/structural_report.md + LifeTrac-v25/mechanical_design/STRUCTURAL_ANALYSIS_LOG.md + retention-days: 90 + + - name: Cleanup Xvfb + if: always() + run: | + if [ -n "$XVFB_PID" ]; then + echo "Stopping Xvfb process (PID: $XVFB_PID)" + # Try graceful shutdown first + kill -TERM $XVFB_PID 2>/dev/null || true + # Wait up to 5 seconds for process to terminate + for i in {1..5}; do + if ! kill -0 $XVFB_PID 2>/dev/null; then + echo "Xvfb stopped gracefully" + break + fi + sleep 1 + done + # Force kill if still running + kill -KILL $XVFB_PID 2>/dev/null || true + echo "Xvfb force stopped" + fi + + - name: Comment on PR with structural analysis + if: github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + + // Read the structural report + let reportContent = 'Structural analysis completed. Check artifacts for detailed results.'; + try { + const report = fs.readFileSync('LifeTrac-v25/mechanical_design/structural_report.md', 'utf8'); + // Extract just the results section + const match = report.match(/## Structural Analysis Results[\s\S]*$/); + if (match) { + reportContent = match[0]; + } + } catch (err) { + console.log('Could not read structural report:', err); + } + + const comment = `## OpenSCAD Structural Analysis Results 🔧 + + ✅ Structural analysis completed! + + ${reportContent} + + ### Persistent Log + A detailed log with failure descriptions has been committed to: + - \`LifeTrac-v25/mechanical_design/STRUCTURAL_ANALYSIS_LOG.md\` + + ### Artifacts + Download the detailed analysis from the workflow artifacts: + - \`structural-analysis\` - Complete logs and report + + **Workflow Run:** [#${{ github.run_number }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) + `; + + github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: comment + }); diff --git a/LifeTrac-v25/mechanical_design/STRUCTURAL_ANALYSIS_LOG.md b/LifeTrac-v25/mechanical_design/STRUCTURAL_ANALYSIS_LOG.md new file mode 100644 index 0000000..75fd358 --- /dev/null +++ b/LifeTrac-v25/mechanical_design/STRUCTURAL_ANALYSIS_LOG.md @@ -0,0 +1,20 @@ +# Structural Analysis Test Log + +This file tracks the history of structural analysis test results. + +--- + +## Latest Analysis + +**Date:** 2025-12-07 07:16:34 UTC +**Commit:** 379a04d9ad945b5817b3ea2a91384fda2ece5e4c +**Branch:** copilot/update-xvfb-installation +**Workflow Run:** https://github.com/OpenSourceEcology/LifeTrac/actions/runs/20000741019 + +### Error + +No structural analysis summary found in output. Check the workflow logs for errors. + +--- + +_This file is automatically updated by the OpenSCAD Structural Analysis workflow._ diff --git a/LifeTrac-v25/mechanical_design/assembly.png b/LifeTrac-v25/mechanical_design/assembly.png index 6da02c9..1d837a4 100644 Binary files a/LifeTrac-v25/mechanical_design/assembly.png and b/LifeTrac-v25/mechanical_design/assembly.png differ