Skip to content

Commit

Permalink
Added standalone mode for SlicerA
Browse files Browse the repository at this point in the history
SlicerA can now run without ArozOS support (some functions are missing, but it works)
  • Loading branch information
TC pushbot 5 authored and TC pushbot 5 committed Apr 24, 2021
1 parent ce58a8f commit f01e376
Show file tree
Hide file tree
Showing 212 changed files with 139,070 additions and 97 deletions.
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,40 @@ A web based STL to Gcode slicer for ArozOS

## Usage

### Use as ArozOS Subservice

To use SlicerA, you can first upload some STL files to your ArozOS cloud desktop and follow the steps below

1. Load STL Model using the top right hand corner button or the "1. Load STL Model " button
2. Click "Slice to Gcode". Wait until it complete and check the finished gcode for any issues in slicing
3. Click "Save to File" if the gcode file looks good.

### Use as standalone web application

To use SlicerA without ArozOS, build the application with standard go building procedures.

```
cd ./SlicerA
go build
```

Next, start the application with the following command

```
./SlicerA
>> SlicerA started. Listening on :80
```

Then, navigate to http://localhost for using the standalone web slicer interface.

You can also change the port where it listen to using - port flag as follows

```
./SlicerA -port 8080
```

**Due to AGPL limitation, there is no Gcode previewer in the standalone mode. Gocde file will be downloaded directly when you press the "Slice to Gcode" button.**



## Screenshots
Expand All @@ -73,6 +101,10 @@ And after export, you can see your gcode file in the location you selected.



Interface under standalone mode

![](img/standalone.png)

## License

Please see the LICENSE file
Expand All @@ -83,3 +115,5 @@ Please see the LICENSE file

This project is powered by the amazing Golang written STL to Gcode slicer named [GoSlice](https://github.com/aligator/GoSlice)

The STL Viewer in standalone mode is powered by [viewstl](https://github.com/omrips/viewstl) licensed under MIT. See web/script/viewstl/LICENSE for more information.

152 changes: 70 additions & 82 deletions execute.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
package main

import (
"encoding/base64"
"encoding/json"
"errors"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
)

Expand Down Expand Up @@ -85,30 +84,43 @@ func handleSaveGcode(w http.ResponseWriter, r *http.Request) {
}
}

//Handle slicing request and return the gcode
func handleSlicing(w http.ResponseWriter, r *http.Request) {
//Get slicing profile
options, err := mv(r, "options", true)
//Slice and return the results, dispose source
func handleSliceAndDispose(w http.ResponseWriter, r *http.Request) {
options, err := mv(r, "options", false)
if err != nil {
sendErrorResponse(w, "Invalid options given")
return
}

//Get the src file
vpath, err := mv(r, "file", true)
stlContent, err := ioutil.ReadAll(r.Body)
if err != nil {
sendErrorResponse(w, "Invalid input file given")
sendErrorResponse(w, "Unable to read stl content")
return
}

//Convert the input vpath to realpath
rpath, err := resolveVirtualPath(w, r, vpath)
//Create a tmp folder
os.MkdirAll("./tmp", 0755)

//Buffer the received base64 stl file into the tmp folder
noheaderContent := strings.ReplaceAll(string(stlContent), "data:application/octet-stream;base64,", "")
dec, err := base64.StdEncoding.DecodeString(noheaderContent)
if err != nil {
sendErrorResponse(w, err.Error())
sendErrorResponse(w, "Unable to read stl content")
return
}

//Parse the option into the required structure
//Write to file
fileID := strconv.Itoa(int(time.Now().Unix()))
tmpFilepath := filepath.Join("./tmp", fileID+".stl")
outFilepath := filepath.Join("./tmp", fileID+".gcode")

err = ioutil.WriteFile(tmpFilepath, dec, 0755)
if err != nil {
sendErrorResponse(w, "Write file to buffer folder failed")
return
}

//Parse the config
thisConfig := config{}
err = json.Unmarshal([]byte(options), &thisConfig)
if err != nil {
Expand All @@ -117,48 +129,61 @@ func handleSlicing(w http.ResponseWriter, r *http.Request) {
return
}

//Get executable base on platform
executable, err := selectBianry()
//Slice the file
err = sliceFile(tmpFilepath, thisConfig, outFilepath)
if err != nil {
sendErrorResponse(w, err.Error())
return
}

//Build the slicing paramter
slicingParamters := []string{rpath} //The first paramter is the stl filepath itself
//Get the output
finalGcode, err := ioutil.ReadFile(outFilepath)
if err != nil {
sendErrorResponse(w, err.Error())
return
}

slicingParamters = append(slicingParamters, "--hot-end-temperature="+strconv.Itoa(thisConfig.HotEndTemperature))
slicingParamters = append(slicingParamters, "--bed-temperature="+strconv.Itoa(thisConfig.BedTemperature))
//Remove the tmp files
os.Remove(tmpFilepath)
os.Remove(outFilepath)

//Calculate the center of the bed
bedCenterX := int(thisConfig.BedWidth * 1000 / 2)
bedCenterY := int(thisConfig.BedDepth * 1000 / 2)
slicingParamters = append(slicingParamters, "--center="+strconv.Itoa(bedCenterX)+"_"+strconv.Itoa(int(bedCenterY))+"_0")
//Send back to sliced Gcode
sendTextResponse(w, string(finalGcode))
}

slicingParamters = append(slicingParamters, "--extrusion-width="+strconv.Itoa(thisConfig.ExtrusionWidth))
slicingParamters = append(slicingParamters, "--filament-diameter="+strconv.Itoa(thisConfig.FilamentDiameter))
slicingParamters = append(slicingParamters, "--layer-thickness="+strconv.Itoa(thisConfig.LayerThickness))
slicingParamters = append(slicingParamters, "--extrusion-multiplier="+strconv.Itoa(thisConfig.ExtrusionMultiplier))
slicingParamters = append(slicingParamters, "--layer-speed="+strconv.Itoa(thisConfig.LayerSpeed))
//Handle slicing request and return the gcode
func handleSlicing(w http.ResponseWriter, r *http.Request) {
//Get slicing profile
options, err := mv(r, "options", true)
if err != nil {
sendErrorResponse(w, "Invalid options given")
return
}

slicingParamters = append(slicingParamters, "--move-speed="+strconv.Itoa(thisConfig.MoveSpeed))
slicingParamters = append(slicingParamters, "--number-top-layers="+strconv.Itoa(thisConfig.NumberTopLayers))
slicingParamters = append(slicingParamters, "--number-bottom-layers="+strconv.Itoa(thisConfig.NumberBottomLayers))
slicingParamters = append(slicingParamters, "--brim-count="+strconv.Itoa(thisConfig.BrimCount))
slicingParamters = append(slicingParamters, "--skirt-count="+strconv.Itoa(thisConfig.SkirtCount))
//Get the src file
vpath, err := mv(r, "file", true)
if err != nil {
sendErrorResponse(w, "Invalid input file given")
return
}

//Initial layer settings
slicingParamters = append(slicingParamters, "--initial-bed-temperature="+strconv.Itoa(thisConfig.InitialBedTemperature))
slicingParamters = append(slicingParamters, "--initial-hot-end-temperature="+strconv.Itoa(thisConfig.InitialHotEndTemperature))
slicingParamters = append(slicingParamters, "--initial-layer-speed="+strconv.Itoa(thisConfig.InitialLayerSpeed))
slicingParamters = append(slicingParamters, "--initial-layer-thickness="+strconv.Itoa(thisConfig.InitialLayerThickness))
//Convert the input vpath to realpath
rpath, err := resolveVirtualPath(w, r, vpath)
if err != nil {
sendErrorResponse(w, err.Error())
return
}

//Use support
if thisConfig.SupportEnabled {
slicingParamters = append(slicingParamters, "--support-enabled")
//Parse the option into the required structure
thisConfig := config{}
err = json.Unmarshal([]byte(options), &thisConfig)
if err != nil {
log.Println(err.Error())
sendErrorResponse(w, "Parse option failed")
return
}

//Execute the slicing to tmp folder
//Get the output filename from ArozOS
tmpFolderAbsolutePath, err := resolveVirtualPath(w, r, "tmp:/SlicerA/")
if err != nil {
sendErrorResponse(w, err.Error())
Expand All @@ -172,51 +197,14 @@ func handleSlicing(w http.ResponseWriter, r *http.Request) {
outputFilename := strconv.Itoa(int(time.Now().Unix())) + ".gcode"
outputFilepath := filepath.Join(tmpFolderAbsolutePath, outputFilename)

//Inject the output filepath to the slicing paramter
slicingParamters = append(slicingParamters, "--output="+outputFilepath)

cmd := exec.Command(executable, slicingParamters...)
out, err := cmd.CombinedOutput()
err = sliceFile(rpath, thisConfig, outputFilepath)
if err != nil {
sendErrorResponse(w, err.Error())
return
}
//OK
log.Println(string(out))

//Return the filename for preview
js, _ := json.Marshal(filepath.ToSlash(filepath.Join("tmp:/SlicerA/", outputFilename)))
sendJSONResponse(w, string(js))

}

func selectBianry() (string, error) {
binaryName := "./goslice/goslice"
binaryExecPath := ""
if runtime.GOOS == "linux" {
if runtime.GOARCH == "arm" {
binaryExecPath = binaryName + "-linux-arm.elf"
} else if runtime.GOARCH == "arm64" {
binaryExecPath = binaryName + "-linux-arm64.elf"
} else if runtime.GOARCH == "386" {
binaryExecPath = binaryName + "-linux-386.elf"
} else if runtime.GOARCH == "amd64" {
binaryExecPath = binaryName + "-linux-amd64.elf"
}

if binaryExecPath != "" {
//Set it to absolute for cross platform compeibility
abspath, _ := filepath.Abs(binaryExecPath)
return abspath, nil
} else {
return "", errors.New("Platform not supported")
}

} else if runtime.GOOS == "windows" {
binaryExecPath = binaryName + "-windows-amd64.exe"
abspath, _ := filepath.Abs(binaryExecPath)
return abspath, nil
} else {
//Not supported
return "", errors.New("Platform not supported")
}
}
Binary file added goslice/goslice-macos-amd64.elf
Binary file not shown.
42 changes: 42 additions & 0 deletions goslice/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
the STL_FILE path has to be specified
Usage of goslice: goslice STL_FILE [flags]
--additional-internal-infill-overlap-percent int The percentage used to make the internal infill (infill not blocked by the perimeters) even bigger so that it grows a bit into the model. (default 400)
--bed-temperature int The temperature for the heated bed after the first layers. (default 55)
--brim-count int The amount of brim lines around the parts of the initial layer.
--center Micrometer The point where the model is finally placed. (default 100000_100000_0)
--extrusion-multiplier int The multiplier in % used to change the amount of filament being extruded. Can be used to mitigate under/over extrusion. (default 100)
--extrusion-width Micrometer The diameter of your nozzle. (default 400)
--fan-speed FanSpeedOptions Comma separated layer/primary-fan-speed. eg. --fan-speed 3=20,10=40 indicates at layer 3 set fan to 20 and at layer 10 set fan to 40. Fan speed can range from 0-255. (default 2=255)
--filament-diameter Micrometer The filament diameter used by the printer. (default 1750)
--finish-polygon-snap-distance Micrometer The max distance between start end endpoint of a polygon used to check if a open polygon can be closed. (default 1000)
--hot-end-temperature int The temperature for the hot end after the first layers. (default 200)
--infill-overlap-percent int The percentage of overlap into the perimeters. (default 50)
--infill-percent int The amount of infill which should be generated. (default 20)
--infill-rotation-degree int The rotation used for the infill. (default 45)
--infill-zig-zag Sets if the infill should use connected lines in zig zag form.
--initial-bed-temperature int The temperature for the heated bed for the first layers. (default 60)
--initial-hot-end-temperature int The filament diameter used by the printer. (default 205)
--initial-layer-speed Millimeter The speed only for the first layer in mm per second. (default 30.000)
--initial-layer-thickness Micrometer The layer thickness for the first layer. (default 200)
--initial-temperature-layer-count int The number of layers which use the initial temperatures. After this amount of layers, the normal temperatures are used. (default 3)
--inset-count int The number of perimeters. (default 2)
--join-polygon-snap-distance Micrometer The distance used to check if two open polygons can be snapped together to one bigger polygon. Checked by the start and endpoints of the polygons. (default 160)
--layer-speed Millimeter The speed for all but the first layer in mm per second. (default 60.000)
--layer-thickness Micrometer The thickness for all but the first layer. (default 200)
--meld-distance Micrometer The distance which two points have to be within to count them as one point. (default 30)
--move-speed Millimeter The speed for all non printing moves. (default 150.000)
--number-bottom-layers int The amount of layers the bottom layers should grow into the model. (default 3)
--number-top-layers int The amount of layers the bottom layers should grow into the model. (default 4)
--outer-perimeter-speed Millimeter The speed only for outer perimeters. (default 40.000)
-o, --output string File path for the output gcode file. Default is the inout file path with .gcode as file ending.
--retraction-length Millimeter The amount to retract in millimeter. (default 2.000)
--retraction-speed Millimeter The speed used for retraction in mm/s. (default 30.000)
--skirt-count int The amount of skirt lines around the initial layer. (default 2)
--skirt-distance Millimeter The distance between the model (or the most outer brim lines) and the most inner skirt line. (default 5.000)
--support-enabled Enables the generation of support structures.
--support-gap Millimeter The gap between the model and the support. (default 0.600)
--support-interface-layers int The amount of layers which are filled differently as interface to the object. (default 2)
--support-pattern-spacing Millimeter The spacing used to create the support pattern. (default 2.500)
--support-threshold-angle int The angle up to which no support is generated. (default 60)
--support-top-gap-layers int The amount of layers without support. (default 3)
-v, --version Print the GoSlice version.
Binary file added img/standalone.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 7 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ func SetupCloseHandler() {
<-c
log.Println("\r- Shutting down SlicerA module")

//Clean up the tmp folder if it exists
if fileExists("./tmp") {
os.RemoveAll("./tmp")
}
os.Exit(0)
}()
}
Expand All @@ -46,8 +50,8 @@ func main() {
LaunchFWDir: "SlicerA/index.html",
SupportEmb: true,
LaunchEmb: "SlicerA/index.html",
InitFWSize: []int{720, 480},
InitEmbSize: []int{720, 480},
InitFWSize: []int{1060, 670},
InitEmbSize: []int{1060, 670},
SupportedExt: []string{".stl"},
})

Expand All @@ -57,6 +61,7 @@ func main() {

//Handle the slicing process
http.HandleFunc("/slice", handleSlicing)
http.HandleFunc("/sliced", handleSliceAndDispose)
http.HandleFunc("/saveGcode", handleSaveGcode)

//Setup the close handler to handle Ctrl+C on terminal
Expand Down
Loading

0 comments on commit f01e376

Please sign in to comment.