Initial commit for i0T.app and migration here with a fresh repo
This commit is contained in:
142
go/internal/printer/parse.go
Normal file
142
go/internal/printer/parse.go
Normal file
@@ -0,0 +1,142 @@
|
||||
package printer
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
tempRe = regexp.MustCompile(`(T\d*|B|C):\s*(-?\d+(?:\.\d+)?)\s*/\s*(-?\d+(?:\.\d+)?)`)
|
||||
sdRe = regexp.MustCompile(`(?i)SD printing byte\s+(\d+)\s*/\s*(\d+)`)
|
||||
notSDRe = regexp.MustCompile(`(?i)Not SD printing`)
|
||||
sdDoneRe = regexp.MustCompile(`(?i)Done printing file`)
|
||||
fileOpen = regexp.MustCompile(`(?i)File opened:\s*(\S+)`)
|
||||
sdListStart = regexp.MustCompile(`(?i)^Begin file list`)
|
||||
sdListEnd = regexp.MustCompile(`(?i)^End file list`)
|
||||
sdListLongRe = regexp.MustCompile(`"([^"]+)"`)
|
||||
)
|
||||
|
||||
// Parse one line of printer output and mutate s. Returns true if anything changed.
|
||||
func (s *State) Parse(line string) bool {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
return false
|
||||
}
|
||||
changed := false
|
||||
|
||||
for _, m := range tempRe.FindAllStringSubmatch(line, -1) {
|
||||
tool := m[1]
|
||||
cur, _ := strconv.ParseFloat(m[2], 64)
|
||||
tgt, _ := strconv.ParseFloat(m[3], 64)
|
||||
if s.Temps == nil {
|
||||
s.Temps = map[string]TempPair{}
|
||||
}
|
||||
s.Temps[tool] = TempPair{Current: cur, Target: tgt}
|
||||
changed = true
|
||||
}
|
||||
|
||||
if s.Firmware == "" {
|
||||
if idx := strings.Index(line, "FIRMWARE_NAME:"); idx >= 0 {
|
||||
rest := line[idx+len("FIRMWARE_NAME:"):]
|
||||
// Cut at the next CAPS_TOKEN: marker that Marlin emits after FIRMWARE_NAME
|
||||
for _, sep := range []string{" SOURCE_CODE_URL:", " PROTOCOL_VERSION:", " MACHINE_TYPE:", " EXTRUDER_COUNT:"} {
|
||||
if i := strings.Index(rest, sep); i >= 0 {
|
||||
rest = rest[:i]
|
||||
}
|
||||
}
|
||||
s.Firmware = strings.TrimSpace(rest)
|
||||
if len(s.Firmware) > 48 {
|
||||
s.Firmware = s.Firmware[:48]
|
||||
}
|
||||
if s.Firmware != "" {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if m := fileOpen.FindStringSubmatch(line); m != nil {
|
||||
s.SDFilename = m[1]
|
||||
changed = true
|
||||
}
|
||||
|
||||
// SD card directory listing (M20). The firmware emits "Begin file list",
|
||||
// one entry per line, then "End file list". While we're inside that
|
||||
// block, every non-marker line is a file entry; we accumulate into
|
||||
// SDFiles. When the block ends we flip SDListing off and leave the
|
||||
// completed slice in place for the UI to consume.
|
||||
if sdListStart.MatchString(line) {
|
||||
s.SDListing = true
|
||||
s.SDFiles = s.SDFiles[:0]
|
||||
changed = true
|
||||
} else if sdListEnd.MatchString(line) {
|
||||
if s.SDListing {
|
||||
s.SDListing = false
|
||||
changed = true
|
||||
}
|
||||
} else if s.SDListing {
|
||||
if f, ok := parseSDFileEntry(line); ok {
|
||||
s.SDFiles = append(s.SDFiles, f)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
|
||||
if m := sdRe.FindStringSubmatch(line); m != nil {
|
||||
byteN, _ := strconv.ParseInt(m[1], 10, 64)
|
||||
total, _ := strconv.ParseInt(m[2], 10, 64)
|
||||
s.SDByte = byteN
|
||||
s.SDTotal = total
|
||||
if byteN > 0 && total > 0 {
|
||||
if s.PrintState != StateSDPrinting && s.PrintState != StatePaused {
|
||||
s.PrintState = StateSDPrinting
|
||||
if s.SDStart.IsZero() {
|
||||
s.SDStart = nowFn()
|
||||
}
|
||||
} else if s.SDStart.IsZero() {
|
||||
s.SDStart = nowFn()
|
||||
}
|
||||
}
|
||||
changed = true
|
||||
} else if notSDRe.MatchString(line) {
|
||||
if s.PrintState == StateSDPrinting {
|
||||
s.PrintState = StateIdle
|
||||
s.SDByte, s.SDTotal = 0, 0
|
||||
s.SDStart = zeroTime
|
||||
changed = true
|
||||
}
|
||||
} else if sdDoneRe.MatchString(line) {
|
||||
s.PrintState = StateIdle
|
||||
s.SDByte = s.SDTotal
|
||||
changed = true
|
||||
}
|
||||
|
||||
return changed
|
||||
}
|
||||
|
||||
// parseSDFileEntry tries to read a single line from inside the
|
||||
// "Begin file list" / "End file list" block. Marlin emits
|
||||
// "FILENAME.GCO 123456" or "FILENAME.GCO 123456 \"Long Name.gcode\"".
|
||||
// Directories are reported with a trailing "/" — we surface them as
|
||||
// entries too so the user can spot them, even though M23 won't enter them.
|
||||
func parseSDFileEntry(line string) (SDFile, bool) {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
return SDFile{}, false
|
||||
}
|
||||
// Strip a trailing long-filename if present, capture it.
|
||||
long := ""
|
||||
if m := sdListLongRe.FindStringSubmatch(line); m != nil {
|
||||
long = m[1]
|
||||
line = strings.TrimSpace(line[:strings.Index(line, "\"")])
|
||||
}
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) == 0 {
|
||||
return SDFile{}, false
|
||||
}
|
||||
name := fields[0]
|
||||
var size int64
|
||||
if len(fields) >= 2 {
|
||||
size, _ = strconv.ParseInt(fields[len(fields)-1], 10, 64)
|
||||
}
|
||||
return SDFile{Name: name, LongName: long, Size: size}, true
|
||||
}
|
||||
Reference in New Issue
Block a user