From 5178e9281e017bb279d537bdfddfc9a0018d4b91 Mon Sep 17 00:00:00 2001 From: dylan Date: Thu, 12 May 2022 22:44:04 +0800 Subject: [PATCH] go version --- .gitignore | 2 + Makefile | 39 ++++++++++++ smfix.go | 169 ++++++++++++++++++++++++++++++++++++++++++++++++++ smfix_test.go | 85 +++++++++++++++++++++++++ 4 files changed, 295 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 smfix.go create mode 100644 smfix_test.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4510cf1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +dist/ +_SLIC3R_ENVS diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5f84cf1 --- /dev/null +++ b/Makefile @@ -0,0 +1,39 @@ +PROJNAME = smfix +LDFLAGS = -w -s +CMD = go build -ldflags="$(LDFLAGS)" +DIST = dist/ + +darwin-arm64: smfix.go + GOOS=darwin GOARCH=arm64 \ + $(CMD) -o $(DIST)$(PROJNAME)-$@ $^ + +darwin-amd64: smfix.go + GOOS=darwin GOARCH=amd64 \ + $(CMD) -o $(DIST)$(PROJNAME)-$@ $^ + +linux-amd64: smfix.go + GOOS=linux GOARCH=amd64 \ + $(CMD) -o $(DIST)$(PROJNAME)-$@ $^ + +linux-arm7: smfix.go + GOOS=linux GOARCH=arm GOARM=7 \ + $(CMD) -o $(DIST)$(PROJNAME)-$@ $^ + +linux-arm6: smfix.go + GOOS=linux GOARCH=arm GOARM=6 \ + $(CMD) -o $(DIST)$(PROJNAME)-$@ $^ + +win64: smfix.go + GOOS=windows GOARCH=amd64 \ + $(CMD) -o $(DIST)$(PROJNAME)-$@ $^ + +win32: smfix.go + GOOS=windows GOARCH=386 \ + $(CMD) -o $(DIST)$(PROJNAME)-$@ $^ + +all: darwin-arm64 darwin-amd64 linux-amd64 linux-arm7 linux-arm6 win64 win32 + @true + +clean: + rm -f $(DIST)$(PROJNAME)-* + diff --git a/smfix.go b/smfix.go new file mode 100644 index 0000000..4457eb9 --- /dev/null +++ b/smfix.go @@ -0,0 +1,169 @@ +package main + +import ( + "bufio" + "bytes" + "fmt" + "io" + "os" + "regexp" + "strconv" +) + +var ( + silent = false + reThumb = regexp.MustCompile(`(?m)(?:^; thumbnail begin \d+[x ]\d+ \d+)(?:\n|\r\n?)((?:.+(?:\n|\r\n?))+?)(?:^; thumbnail end)`) + in *os.File + out io.Writer +) + +var ( + sliLayerHeight = os.Getenv("SLIC3R_LAYER_HEIGHT") + sliBedTemp = os.Getenv("SLIC3R_BED_TEMPERATURE") + sliFirstBedTemp = os.Getenv("SLIC3R_FIRST_LAYER_BED_TEMPERATURE") + sliTemp = os.Getenv("SLIC3R_TEMPERATURE") + sliFirstTemp = os.Getenv("SLIC3R_FIRST_LAYER_TEMPERATURE") + sliPrintSpeedSec = os.Getenv("SLIC3R_MAX_PRINT_SPEED") +) + +func convertThumbnail(gcodes [][]byte) []byte { + comments := bytes.NewBuffer([]byte{}) + for _, line := range gcodes { + if len(line) > 0 && line[0] == ';' { + comments.Write(line) + comments.WriteRune('\n') + } + } + matches := reThumb.FindAllSubmatch(comments.Bytes(), -1) + if matches != nil { + none := []byte(nil) + data := matches[len(matches)-1][1] + data = bytes.ReplaceAll(data, []byte("\r\n"), none) + data = bytes.ReplaceAll(data, []byte("\n"), none) + data = bytes.ReplaceAll(data, []byte("; "), none) + b := []byte("data:image/png;base64,") + return append(b, data...) + } + return nil +} + +func findEstimatedTime(gcodes [][]byte) int { + for _, line := range gcodes { + if 0 == bytes.Index(line, []byte("; estimated printing time")) { + est := line[bytes.Index(line, []byte("= "))+2:] // 2d 12h 8m 58s + est = bytes.ReplaceAll(est, []byte(" "), []byte(nil)) + t := map[byte]int{'d': 0, 'h': 0, 'm': 0, 's': 0} + for _, p := range []byte("dhms") { + if i := bytes.IndexByte(est, p); i >= 0 { + t[p], _ = strconv.Atoi(string(est[0:i])) + est = est[i+1:] + } + } + return t['d']*86400 + + t['h']*3600 + + t['m']*60 + + t['s'] + } + } + return 0 +} + +func fix() { + lineCount := 0 + buf := &bytes.Buffer{} + buf.ReadFrom(in) + gcodes := [][]byte{} + for { + line, err := buf.ReadBytes('\n') + if err != nil { + break + } + gcodes = append(gcodes, line[0:len(line)-1]) + lineCount++ + } + in.Close() + + if lineCount == 0 { + usage() + } + + speed, _ := strconv.Atoi(sliPrintSpeedSec) + + headers := [][]byte{ + []byte(";Header Start"), + []byte(";FAVOR:Marlin"), + []byte(fmt.Sprintf(";Layer height: %s", sliLayerHeight)), + []byte(";header_type: 3dp"), + []byte(";"), // slot for thumbnail + []byte(fmt.Sprintf(";file_total_lines: %d", lineCount)), + []byte(fmt.Sprintf(";estimated_time(s): %d", findEstimatedTime(gcodes))), + []byte(fmt.Sprintf(";nozzle_temperature(°C): %s", sliTemp)), + []byte(fmt.Sprintf(";build_plate_temperature(°C): %s", sliBedTemp)), + []byte(fmt.Sprintf(";work_speed(mm/minute): %d", speed*60)), + []byte(";Header End\n\n"), + } + + thumbnail := convertThumbnail(gcodes) + if thumbnail != nil { + headers[4] = append([]byte(";thumbnail: "), thumbnail...) + } + + bw := bufio.NewWriter(out) + bw.Write(bytes.Join(headers, []byte("\n"))) + bw.Write(bytes.Join(gcodes, []byte("\n"))) + bw.Flush() +} + +func usage() { + fmt.Println("smfix, optimize G-code file for Snapmaker 2.") + fmt.Println("") + fmt.Println("Example:") + fmt.Println("# smfix a.gcode") + fmt.Println("or") + fmt.Println("# cat a.gcode | smfix > b.gcode") + fmt.Println("") + os.Exit(1) +} + +func main() { + defer func() { + if r := recover(); r != nil { + fmt.Fprintf(os.Stderr, "Err: %s", r) + os.Exit(2) + } + }() + + if len(os.Args) > 1 { + var err error + in, err = os.Open(os.Args[1]) + if err != nil { + fmt.Println(err) + return + } + + out, err = os.OpenFile(os.Args[1], os.O_WRONLY|os.O_CREATE, 0600) + if err != nil { + fmt.Println(err) + return + } + + } else if st, _ := os.Stdin.Stat(); (st.Mode() & os.ModeCharDevice) == 0 { + silent = true + in = os.Stdin + out = os.Stdout + } + + if in == nil { + usage() + } + + if !silent { + fmt.Print("Starting SMFix ... ") + } + + fix() + + if !silent { + fmt.Println("done") + } +} diff --git a/smfix_test.go b/smfix_test.go new file mode 100644 index 0000000..4793326 --- /dev/null +++ b/smfix_test.go @@ -0,0 +1,85 @@ +package main + +import ( + "bytes" + "testing" +) + +func split(s string) (gcodes [][]byte) { + b := bytes.NewBufferString(s) + for { + line, err := b.ReadBytes('\n') + if err != nil { + break + } + gcodes = append(gcodes, line[0:len(line)-1]) + } + return gcodes +} + +func TestConvertThumbnail(t *testing.T) { + gcodes := split(`; top_infill_extrusion_width = 0.4 +; top_solid_infill_speed = 60% +; top_solid_layers = 6 +; generated by PrusaSlicer 2.4.2+arm64 on 2022-05-12 at 06:33:34 UTC +G0 +G1 + +; +; thumbnail begin 16x16 536 +; aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +; bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +; thumbnail end +; +G0 +G1 +; +; thumbnail begin 220x124 6528 +; xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +; yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy +; zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz +; thumbnail end +; +; + +; external perimeters extrusion width = 0.45mm +; perimeters extrusion width = 0.45mm +; infill extrusion width = 0.45mm + +`) + + comp := []byte("data:image/png;base64,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz") + r := convertThumbnail(gcodes) + if 0 != bytes.Compare(r, comp) { + t.Error(r, comp) + } +} + +func TestFindEstimatedTime(t *testing.T) { + gcodes := split(`; estimated printing time = 2s + `) + if r := findEstimatedTime(gcodes); 2 != r { + t.Error("2s /", r) + } + gcodes = split(`; estimated printing time = 1m 2s + `) + if r := findEstimatedTime(gcodes); 60+2 != r { + t.Error("1m 2s /", r) + } + gcodes = split(`; estimated printing time = 1h 2s + `) + if r := findEstimatedTime(gcodes); 3600+2 != r { + t.Error("1h 2s /", r) + } + gcodes = split(`; estimated printing time = 2d 1m 2s + `) + if r := findEstimatedTime(gcodes); 2*86400+1*60+2 != r { + t.Error("2d 1m 2s /", r) + } + gcodes = split(`; estimated printing time(first) = 2d +; estimated printing time = 2d 1m 3s + `) + if r := findEstimatedTime(gcodes); 2*86400 != r { + t.Error("2d /", r) + } +}