def test_weird_comments(self): gcode = GCode() gcode.load_file("gc_weird_comments.gcode") # check correct number of commands self.assertEqual(len(gcode._lines), 6) g_lines = 0 for i in range(6): if gcode._lines[i].has_word("G"): g_lines += 1 # check correct command recognition self.assertTrue(gcode._lines[i].get_word("G"), 1) # check if parameters were found self.assertEqual(gcode._lines[i].has_word("X"), True) self.assertEqual(gcode._lines[i].has_word("Y"), True) self.assertEqual(gcode._lines[i].has_word("E"), True) # check if values were found self.assertEqual(gcode._lines[i].get_word("X"), 50) self.assertEqual(gcode._lines[i].get_word("Y"), 50) self.assertEqual(gcode._lines[i].get_word("E"), 50.75) # check validity self.assertEqual(gcode._lines[i].is_valid(), True) self.assertEqual(g_lines, 4) for i in range(5): # check if a comment was found (last line has none there fore only range(5)) self.assertNotEqual(gcode._lines[i].comment, None)
def __init__(self, controller): super(Slic3rEngineRunner, self).__init__() self.is_running = True self.controller = controller self.gcode = GCode(self.controller.app_config.tmp_place + 'out.gcode', self.controller, None, None) system_platform = platform.system() if system_platform in ['Linux']: self.slicer_place = ['/home/tibor/dev/Slic3r/bin/slic3r'] #self.slicer_place = './tools/Slic3r-Lite/slic3r' elif system_platform in ['Darwin']: self.slicer_place = [ self.controller.app_config.local_path + "tools/Slic3r-Lite/Slic3r.app/Contents/MacOS/Slic3r" ] elif system_platform in ['Windows']: self.slicer_place = ['tools\\Slic3r-Lite\\slic3r-noconsole.exe'] else: self.slicer_place = ['slic3r'] #print(self.slicer_place) self.step_max = 9 self.step = 0
def test_g01_equal_g1(self): gcode = GCode() gcode.load_file("gc_g1_equals_g01.gcode") self.assertEqual(len(gcode._lines), 3) # three commands should be loaded for i in range(3): self.assertEqual(gcode._lines[i].get_word("G"), 1)
def load_text2(z): s = 0.3 t1 = GCode.from_file('text/Home is where.nc').depth(G0Z, z) t2 = GCode.from_file('text/your stop is.nc').depth(G0Z, z) t1 = t1.scale(s, s) t2 = t2.scale(s, s) t1 = t1.move(3, 2.75, 0.5, 0.5) t2 = t2.move(3, 2.25, 0.5, 0.5) return t1 + t2
def test_multi_int(self): gcode = GCode() gcode.load_file("gc_multi_int.gcode") # check correct number of commands self.assertEqual(len(gcode._lines), 12) cnt = 0 for i in range(len(gcode._lines)): if gcode._lines[i].is_comment_only(): cnt += 1 self.assertEqual(cnt, 2) # two lines should contain only a comment # check correct command recognition self.assertEqual(gcode._lines[1].get_word("G"), 1) self.assertEqual(gcode._lines[2].get_word("G"), 0) self.assertEqual(gcode._lines[3].get_word("G"), 10) self.assertEqual(gcode._lines[4].get_word("G"), 16) self.assertEqual(gcode._lines[5].get_word("G"), 1) self.assertEqual(gcode._lines[6].get_word("G"), 1) self.assertEqual(gcode._lines[7].get_word("G"), 1) self.assertEqual(gcode._lines[9].get_word("G"), 0) self.assertEqual(gcode._lines[10].get_word("G"), 1) self.assertEqual(gcode._lines[11].get_word("G"), 17) for i in range(1, 8): # batch check for lines 1 to 7 # check if parameters were found self.assertEqual(gcode._lines[i].has_word("X"), True) self.assertEqual(gcode._lines[i].has_word("Y"), True) # check if values were found self.assertEqual(gcode._lines[i].get_word("X"), 500) self.assertEqual(gcode._lines[i].get_word("Y"), 400) # check validity self.assertEqual(gcode._lines[i].is_valid(), True) for i in range(1,4): # check if comment was found by checking for "Test comment" self.assertEqual("Test comment" in gcode._lines[i].comment, True) ################################### # check the last three lines extra # # weird but valid line formatting (NIST example) self.assertEquals(gcode._lines[9].get_word("X"), 0.1234) self.assertEquals(gcode._lines[9].get_word("Y"), 7) # negative signs self.assertEqual(gcode._lines[10].get_word("X"), -5) self.assertEqual(gcode._lines[10].get_word("Y"), -0.17) # this is the no-hex-check self.assertEqual(gcode._lines[11].get_word("X"), 500) self.assertEqual(gcode._lines[11].get_word("Y"), 0)
def load_text1(z): s = 0.3 t1 = GCode.from_file('text/TransLoc.nc').depth(G0Z, z) t2 = GCode.from_file('text/OnDemand.nc').depth(G0Z, z) t1 = t1.scale(s, s) t2 = t2.scale(s, s) t1 = t1.move(3 - 0.125, 6.75, 1, 0.5) t2 = t2.move(3 + 0.125, 6.75, 0, 0.5) return t1 + t2
def load_data(self, filename): # initalizes a virtual machine from the gcode in the file given # all path data for this gcode is calculated; this is a cpu intensive task! self.gcode = GCode() self.gcode.load_file(filename) self.machine = Machine(self.gcode, self.profilecon) self.machine.create_path() # set the layer sliders maximum to represent the given amount of layers and enable the slider self.layerSlider.setMaximum(len(self.machine.layers) - 1) self.layerSlider.setEnabled(True)
def run_file(self, fname): print("Currently not supported. Use the web interface!") return g = GCode(self.cfg, fname) g.set_zero_extruder(self.zero_extruder) m = Move(self.cfg, g, self.printer) self.preheat() self.sc = StepperCluster(self.audiodev, self.dim, self.cfg, m) self.sc.set_speed_scale(self.speed_scale) self.sc.set_max_feedrate(self.limit) self.scd = StepperClusterDispatcher(self.sc, self)
def create_piece(piece, mirror): bit_diameter = 0.125 corner_radius = 0.1875 groove_depth = 0.0625 cells = [cell(x, y) for x, y in piece] mp = cascaded_union(cells).buffer(-corner_radius).buffer(corner_radius) g = GCode() for p in cells: g += GCode.from_geometry(p, G0Z, -groove_depth) g += GCode.from_geometry(mp.buffer(bit_diameter / 2), G0Z, -0.4, bit_diameter + 0.2, 3.5) if mirror: g = g.scale(-1, 1) g = g.origin() return g
def main(): g0z = 0.2 g1z = -0.01 w = 32 h = 6 g = GCode() for i, (name, au, radius) in enumerate(PLANETS): p = (au + 0.25) / (PLANETS[-1][1] + 1) x = w * p t = GCode.from_file('text/%s.nc' % name) t = t.depth(0.2, -0.01) t = t.rotate(90).scale(0.2, 0.2) t = t.move(x, 0.75, 0.5, 0.5) _, ty1, _, ty2 = t.bounds g += t if i > 0: r = 1.2 * radius / 71492.68 y1 = h / 2 - r y2 = h / 2 + r g += GCode(['G0 X%f Y%f' % (x, y1), 'G3 X%f Y%f I%f J%f' % (x, y2, 0, r), 'G3 X%f Y%f I%f J%f' % (x, y1, 0, -r)]) g += GCode.from_points([(x, 0), (x, ty1 - 0.1)], g0z, g1z) g += GCode.from_points([(x, ty2 + 0.1), (x, y1-0.1)], g0z, g1z) g += GCode.from_points([(x, y2+0.1), (x, h)], g0z, g1z) else: g += GCode.from_points([(x, 0), (x, ty1 - 0.1)], g0z, g1z) g += GCode.from_points([(x, ty2 + 0.1), (x, h)], g0z, g1z) im = g.render(0, 0, 32, 6, 96) im.write_to_png('planets.png')
def generate_text(name, x, y, scale, angle): g = GCode.from_file('text/%s.nc' % name) g = g.depth(G0Z, G1Z_TEXT) g = g.scale(scale, scale) g = g.rotate(angle) g = g.move(x, y, 0.5, 0.5) return g
def coaster(style, side, corner_radius, circle_diameter, circle_depth, depth, bit): n = 120 points = filled_circle(circle_diameter / 2.0 - bit / 2.0, n, bit / 4.0) g = GCode.from_points(points, G0Z, -circle_depth) if style == 1: points = rounded_square(side + bit, corner_radius) elif style == 2: points = circle(side / 2.0, n) elif style == 3: points = rounded_polygon(6, side / 2.0, corner_radius) elif style == 4: points = wavy_circle(side / 2.0, 360, 0.2, 7) else: raise Exception('invalid style') g += GCode.from_points(points, G0Z, -depth).multipass(G0Z, -depth, bit) return g.origin()
def parse(code, ifile, dump, ofile, stats, noopt): if not ifile and not code: print("Need either file or code") return -1 if ifile: code = ifile.read() else: code = '\n'.join(code.split(';')) codes = parse_and_optimize(code, noopt) absolute = GStatement(GCode('G', 90)) codes.insert(0, absolute) if stats: print(generate_stats(codes), file=sys.stderr) s = [] for statement in codes: s.append(str(statement)) if ofile: ofile.write('\n'.join(s)) if dump: print('\n'.join(s), )
def load_letter(letter): if letter.isdigit(): letter = 'NUM' + letter wkt = getattr(foam, letter) polygon = loads(wkt) g = GCode.from_geometry(polygon, 0.2, -0.1875) g = g.scale(6, 6).move(3, 4, 0.5, 0.5) return g
def load_marker(z): sx, sy = W / 600, H / 800 s = min(sx, sy) polygon = loads(WKT) polygon = scale(polygon, s, s) polygon = polygon.buffer(0.125) g = GCode.from_geometry(polygon, G0Z, z) g = g.move(3, 4, 0.5, 0.5) return g
def create_mounts(): a = 116.56505 p = create_mount(5, 3.5, 1, 0.125, a) g = GCode.from_geometry(p, G0Z, -0.1) g1 = g.move(3, 0.25, 0.5, 0) g2 = g.origin().rotate(180).move(3, 5.5, 0.5, 1) g = g1 + g2 g = g.depth(G0Z, -0.2) + g.depth(G0Z, -0.4) + g.depth(G0Z, -0.6) g = HEADER + g + FOOTER g.save('mounts.nc') im = g.render(0, 0, 6, 8, 96) im.write_to_png('mounts.png')
def create_supports(): n = 18 a = 116.56505 p = create_support(1, 0.125, a) g = GCode.from_geometry(p, G0Z, -0.1) g = g.origin() g = pack_gcodes([g] * n, 6, 8, 0.125)[0] g = g.depth(G0Z, -0.2) + g.depth(G0Z, -0.4) + g.depth(G0Z, -0.6) g = HEADER + g + FOOTER g.save('supports.nc') im = g.render(0, 0, 6, 8, 96) im.write_to_png('supports.png')
def GCodeInfo(self, gcodeFile): gcode = GCode(open(gcodeFile, "rU")) xdims = (gcode.xmin, gcode.xmax, gcode.width) ydims = (gcode.ymin, gcode.ymax, gcode.depth) zdims = (gcode.zmin, gcode.zmax, gcode.height) filamenUse = gcode.filament_length layersN = gcode.layers_count estimateTime = gcode.estimate_duration()[1] gridData = [ ['GCode', gcodeFile], ['Dimensions X', ("Min %0.02f - Max %0.02f (%0.02f)" % xdims)], ['Dimensions Y', ("Min %0.02f - Max %0.02f (%0.02f)" % ydims)], ['Dimensions Z', ("Min %0.02f - Max %0.02f (%0.02f)" % zdims)], ['Filament used', ("%0.02f mm - %0.02f cm" % (filamenUse, filamenUse/100))], ['Number of layers', (" %d" % layersN)], ['Estimated duration: ', ("%s" % estimateTime)] ] return gridData
def test_single_int(self): gcode = GCode() gcode.load_file("gc_single_int.gcode") # check if only one command self.assertEqual(len(gcode._lines), 1) # check command recognized correctly self.assertTrue(gcode._lines[0].has_word("G")) self.assertEqual(gcode._lines[0].get_word("G"), 5) # check parameters recognized correctly and upper/lowercase compatibility self.assertEqual(gcode._lines[0].has_word("X"), True) self.assertEqual(gcode._lines[0].has_word("x"), True) self.assertEqual(gcode._lines[0].has_word("Y"), True) self.assertEqual(gcode._lines[0].has_word("y"), True) self.assertEqual(gcode._lines[0].get_word("X"), 500) self.assertEqual(gcode._lines[0].get_word("Y"), 400) # check validity self.assertEqual(gcode._lines[0].is_valid(), True)
def __init__(self, cfg): self.cfg = cfg self.webui = None self.gcode_queue = queue.Queue(100) self.gcode_file = None self.idling = True self.pause = False self.ev_buffer = asyncio.Event() self.pid = {} self.setpoint = {} self.setpoint_fail_time = {} self.tolerance = 3 self.current_status = None self.heater_enable_mcodes = True self.heater_disable_eof = True self.ignore_endstop = False self.prepare_endswitches() self.machine_ready = False self.move = Move(self.cfg, self) self.gcode = GCode(self.cfg) dim = self.cfg.settings["num_motors"] audiodev = self.cfg.settings["sound_device"] self.current_e = 0 self.extruder_safety_timeout = 300 # FIXME self.extruder_safety_time = time.time() + self.extruder_safety_timeout for n in ["ext", "bed"]: name = n.upper() o = GPOutput("heater_" + name) s = ScaledSensor(self.cfg, name) t = Thermistor100k(s) self.pid[n] = PidController(t, o, 0.3, 0.004, 0.5) self.launch_pid(n, 20) self.loop = asyncio.get_event_loop() self.sc = StepperCluster(audiodev, dim, self.cfg, self.esw) self.sc.connect_cmd_buffer(self.move.get_output_buffer()) self.loop.add_writer(self.sc.fileno(), self.handle_sc_write) asyncio. async (self.gcode_processor()) asyncio. async (self.coro_check_machine())
def create_s(): c1 = create_circle(0, 1.03, 1, 20, 270-15) c2 = create_circle(0, -1.03, 1, 200, 450-15) s = LineString(c1 + list(reversed(c2))) s = s.buffer(0.4) g = GCode.from_geometry(s, 0, 0) g = g.scale_to_fit(6, 8) g = g.move(3, 4, 0.5, 0.5) g1 = g.depth(0.2, -0.3) g2 = g.depth(0.2, -0.6) g = HEADER + g1 + g2 + FOOTER g.save('sophia.nc') im = g.render(0, 0, 6, 8, 96) im.write_to_png('sophia.png')
def main(): tw, th = 6, 8 w, h = 24-2, 24-2 p = 0 shapes = load_shapes() shapes.append(circle(R)) shapes.append(circle(R * 1.05)) mp = MultiLineString(shapes) mp = fit_shape(mp, w, h, p) g = GCode.from_geometry(mp, G0Z, G1Z) im = g.render(0, 0, w, h, 96) im.write_to_png('hemi.png') for i in range(4): for j in range(3): print i, j tile = create_tile(i, j, tw, th) tmp = intersection(mp, tile) g = GCode.from_geometry(tmp, G0Z, G1Z) g = g.translate(-i * tw, -j * th) g = HEADER + g + FOOTER im = g.render(0, 0, tw, th, 96) im.write_to_png('hemi-tiles/%d.%d.png' % (j, i)) g.save('hemi-tiles/%d.%d.nc' % (j, i))
def create_face(dihedral, sides, length, depth, iterations): angle = radians((180 - dihedral) / 2) hyp = 1 / cos(angle) * depth max_offset = sin(angle) * hyp points = [] for i in range(iterations): p = i / float(iterations - 1) offset = sin(angle) * p * hyp z = -cos(angle) * p * hyp p = create_points(sides, length, offset - max_offset) p = [(x, y, z) for x, y in p] points.extend(p) lines = [] lines.append('G0 Z%f' % G0Z) lines.append('G0 X%f Y%f' % points[0][:2]) for point in points: lines.append('G1 X%f Y%f Z%f' % point) lines.append('G0 Z%f' % G0Z) g = GCode(lines) g = g.origin() g = HEADER + g + FOOTER g.save('polyhedra.nc') im = g.render(0, 0, 6, 8, 96) im.write_to_png('polyhedra.png')
def generate_county(shape, name, text): result = [] polygons = get_polygons(shape, SCALE) max_polygon = max(polygons, key=attrgetter('area')) for i, polygon in enumerate(polygons): g = GCode.from_geometry(polygon, G0Z, G1Z_BEVEL) if text and polygon == max_polygon: x, y = polygon.centroid.coords[0] dx, dy = TEXT_OFFSETS.get(name, (0, 0)) scale = TEXT_SIZES.get(name, TEXT_SIZE) angle = TEXT_ANGLES.get(name, 0) g += generate_text(name, x + dx, y + dy, scale, angle) g = g.origin() g.name = ('%s %d' % (name, i)) if i else name result.append(g) return result
def main2(): bit = 0.0625 s = 1.82748538 for letter in 'MEGAN': p = load_letter(letter) p = scale(p, s, s) p = p.buffer(bit / 2) g = GCode.from_geometry(p, G0Z, -bit) g = g.origin() depths = [-bit, -bit*2, -bit*3, -bit*4] gs = [g.depth(G0Z, d) for d in depths] g = reduce(operator.add, gs) g = HEADER + g + FOOTER im = g.render(0, 0, 6, 8, 96) im.write_to_png('megan-%s.png' % letter) g.save('megan-%s.nc' % letter)
def create_shape(sides, length): p = create_points(sides, length) g = GCode() g += GCode.from_points(p, G0Z, -0.24) # g += GCode.from_points(p, G0Z, -0.4) # g += GCode.from_points(p, G0Z, -0.5) # g = g.rotate_and_scale_to_fit(6.5, 8) # g = g.rotate(180) g = g + g.translate(0, 4) g = g.origin() g = HEADER + g + FOOTER g.save('solids.nc') im = g.render(0, 0, 6.5, 8, 96) im.write_to_png('solids.png')
def main(): bit = 0.125 r = 0.21875 d = 0.09375 steps = 16 cells = [ cell(0, 0), cell(1, 0), cell(1, 1), cell(2, 1), cell(3, 1), ] mp = cascaded_union(cells).buffer(-bit).buffer(bit) g = GCode() for p in cells: g += GCode.from_geometry(p, G0Z, -d) # for p in cells: # g += GCode.from_geometry(p, G0Z, -bit) for step in range(steps): p = step / (steps - 1.0) a = radians(p * 90) x = sin(a) * r b = x + bit / 2 - r z = r - (r * r - x * x) ** 0.5 print '%.3f, %.3f, %.3f, %.3f' % (p, x, b, z) g += GCode.from_geometry(mp.buffer(b), G0Z, -z) # g += GCode.from_geometry(mp.buffer(bit / 2), G0Z, -0.4) # g += GCode.from_geometry(mp.buffer(bit / 2), G0Z, -0.7) # g += GCode.from_geometry(mp.buffer(bit / 2), G0Z, -0.6, bit + 0.1, 3) g += GCode.from_geometry(mp.buffer(bit / 2), G0Z, -0.4, bit + 0.2, 3.5) g = g.scale(-1, 1) g = g.origin().translate(0, 0.5) g = HEADER + g + FOOTER p = 0#0.5 im = g.render(-p, -p, 6 + p, 6 + p, 96*4) im.write_to_png('blokus.png') g.save('blokus.nc')
def best_scale(width, height): result = None shapes = load_county_shapes('37') for county in COUNTIES: shape = shapes[county.name] polygons = get_polygons(shape, 1) for polygon in polygons: sizes = [] g = GCode.from_geometry(polygon, 0, 0) for angle in range(0, 180, 5): w, h = g.rotate(angle).size size = min(width / w, height / h) sizes.append((size, angle)) size = max(sizes) print county.name, size if result is None or size < result: result = size print result return result
def main(): # 8x3 i, j = 5, 1 tw, th = 6, 8 w, h = 48-2, 24-2 p = 0 shapes = load_shapes() mp = MultiPolygon(shapes) mp = fit_shape(mp, w, h, p) for i in range(7): for j in range(3): print i, j tile = create_tile(i, j, tw, th) tmp = intersection(mp, tile) g = GCode.from_geometry(tmp, G0Z, G1Z) g = g.translate(-i * tw, -j * th) g = HEADER + g + FOOTER im = g.render(0, 0, tw, th, 96) im.write_to_png('usa-tiles/%d.%d.png' % (j, i)) g.save('usa-tiles/%d.%d.nc' % (j, i))
def training_batches(min_sequence=64, max_sequence=2048): filenames = ['./data/Killer_Queen_Jojo_Skull_Redo.stl'] gcodes = [ g.normalize_moves().relative_moves() for f in filenames for g in GCode.stl_to_gcode(f) ] datas = list(map(gcode_to_numpy, gcodes)) del gcodes def _(): for data in datas: nlines = data.shape[0] i = 0 while nlines > min_sequence: upper = min(max_sequence or nlines, nlines) - 1 seqlen = random.randint(min_sequence, upper) yield (data[i:i + seqlen], data[i + seqlen]) nlines -= seqlen + 1 i += seqlen + 1 return _
def __init__(self, controller): super(Slic3rEngineRunner, self).__init__() self.is_running = True self.controller = controller self.gcode = GCode(self.controller.app_config.tmp_place + 'out.gcode', self.controller, None, None) system_platform = platform.system() if system_platform in ['Linux']: self.slicer_place = [self.controller.app_config.local_path + "tools/Slic3r-Lite/bin/slic3r"] elif system_platform in ['Darwin']: self.slicer_place = [self.controller.app_config.local_path + "tools/Slic3r-Lite/Slic3r.app/Contents/MacOS/Slic3r"] elif system_platform in ['Windows']: self.slicer_place = ['tools\\Slic3r-Lite\\slic3r-noconsole.exe'] else: self.slicer_place = ['slic3r'] #print(self.slicer_place) self.step_max = 9 self.step = 0
def main(): bit = 0.25 mp = load_letters('MEGAN') mp = mp.buffer(-bit / 2) mps = [] while not mp.is_empty: mps.append(mp) mp = mp.buffer(-bit / 2) g = GCode() for mp in mps: g += GCode.from_geometry(mp, G0Z, -0.21875 * 1.0) g = g.rotate(90).origin().translate(2, 0) g = HEADER + g + FOOTER im = g.render(0, 0, 6, 8, 96) im.write_to_png('megan.png') g.save('megan.nc')
def main(): counties = load_polygons(COUNTY_SHAPEFILE) state = load_polygons(STATE_SHAPEFILE) for y in range(4): for x in range(14): tile = create_tile(x, y, 6, 8) county_shapes = intersection(counties, tile) state_shapes = intersection(state, tile, -0.25) if not county_shapes and not state_shapes: continue print x, y, len(county_shapes), len(state_shapes) g = GCode() for shape in county_shapes: g += GCode.from_geometry(shape, G0Z, G1Z_COUNTY) for shape in state_shapes: g += GCode.from_geometry(shape, G0Z, G1Z_STATE1) for shape in state_shapes: g += GCode.from_geometry(shape, G0Z, G1Z_STATE2) g = g.translate(-tile.bounds[0], -tile.bounds[1]) g = HEADER + g + FOOTER g.save('tiles/%02d.%02d.nc' % (y, x)) p = 0.1 surface = g.render(0 - p, 0 - p, 6 + p, 8 + p, 96) surface.write_to_png('tiles/%02d.%02d.png' % (y, x))
def __init__(self, cfg): self.cfg = cfg self.webui = None self.gcode_queue = queue.Queue(100) self.gcode_file = None self.idling = True self.pause = False self.ev_buffer = asyncio.Event() self.pid = {} self.setpoint = {} self.setpoint_fail_time = {} self.tolerance = 3 self.current_status = None self.heater_enable_mcodes = True self.heater_disable_eof = True self.ignore_endstop = False self.prepare_endswitches() self.machine_ready = False self.move = Move(self.cfg, self) self.gcode = GCode(self.cfg) dim = self.cfg.settings["num_motors"] audiodev = self.cfg.settings["sound_device"] self.current_e = 0 self.extruder_safety_timeout = 300 # FIXME self.extruder_safety_time = time.time() + self.extruder_safety_timeout for n in ["ext", "bed"]: name = n.upper() o = GPOutput("heater_" + name) s = ScaledSensor(self.cfg, name) t = Thermistor100k(s) self.pid[n] = PidController(t, o, 0.3, 0.004, 0.5) self.launch_pid(n, 20) self.loop = asyncio.get_event_loop() self.sc = StepperCluster(audiodev, dim, self.cfg, self.esw) self.sc.connect_cmd_buffer(self.move.get_output_buffer()) self.loop.add_writer(self.sc.fileno(), self.handle_sc_write) asyncio.async(self.gcode_processor()) asyncio.async(self.coro_check_machine())
def main(): sep = GCode.from_points([(3, 0), (3, 8)], 0.2, -0.05) t1 = GCode.from_file('text/Planet Earth.nc') t2 = GCode.from_file('text/Lambert Azimuthal Equal-Area.nc') t3 = GCode.from_file('text/0 N 110 W.nc') t4 = GCode.from_file('text/Michael Fogleman.nc') t5 = GCode.from_file('text/July 2015.nc') t1 = t1.scale(1.25, 1.25) t2 = t2.scale(0.55, 0.55) t3 = t3.scale(0.55, 0.55) t1 = t1.move(0, 0, 0.5, 0.5).depth(0.2, -0.07) t2 = t2.move(0, 0, 0.5, 0.5).depth(0.2, -0.03) t3 = t3.move(0, 0, 0.5, 0.5).depth(0.2, -0.03) t4 = t4.move(0, 0, 0.5, 0.5).depth(0.2, -0.05) t5 = t5.move(0, 0, 0.5, 0.5).depth(0.2, -0.05) t1 = t1.translate(0, 6.5) t2 = t2.translate(0, 5) t3 = t3.translate(0, 4) t4 = t4.translate(0, 2) t5 = t5.translate(0, 0) t = t1 + t2 + t3 b = t4 + t5 s = 2.0 / 3.0 t = t.scale(s, s) b = b.scale(s, s) t = t.rotate(90) b = b.rotate(90) t = t.move(1.5, 4, 0.5, 0.5) b = b.move(4.5, 4, 0.5, 0.5) t = t + b t = t + sep t = HEADER + t + FOOTER p = 0.25 im = t.render(0-p, 0-p, 6+p, 8+p, 96) im.write_to_png('engrave.png') t.save('engrave.nc')
def create_shape(sides, length): bit = 0.125 size = 0.25 offset = bit / 5 data = [ [0] * 5, # [1] * 5, # [0, 1, 0, 0, 1], # [1, 0, 1, 1, 0], ] # gs = GCode() for index, directions in enumerate(data): p = create_points(sides, length) # p = add_notches(p, size, offset, directions) # p = LineString(p).buffer(bit / 2).exterior.coords g = GCode() g += GCode.from_points(p, G0Z, -0.15) g += GCode.from_points(p, G0Z, -0.275) # g = g.move(3, 0, 0.5, 0) # gs += g g = HEADER + g + FOOTER g.save('solids%d.nc' % index) im = g.render(0, 0, 6, 8, 96) im.write_to_png('solids%d.png' % index)
import logging import re from extruder import Extruder from gcode import GCode from layer import FirstLayer, ACT_INFILL, ACT_PASS, ACT_SWITCH, Layer import utils from gcode_file import SLICER_SIMPLIFY3D, GCodeFile from settings import Settings gcode = GCode() log = logging.getLogger("S3DSlicer") class Simplify3dGCodeFile(GCodeFile): slicer_type = SLICER_SIMPLIFY3D LAYER_START_RE = re.compile(b".*layer (\d+), Z = (\d+\.*\d*)") VERSION_RE = re.compile(b".*Version (\d)\.(\d)\.(\d)") def __init__(self, logger, settings: Settings): super().__init__(logger, settings) self.extruder_name = [] self.extruder_tool = [] self.extruder_diameter = [] self.extruder_multiplier = [] self.extruder_use_retract = [] self.extruder_retract_dist = [] self.extruder_retract_speed = [] self.extruder_zhop = []
def send(code, ifile, device, baudrate, measure, yes, noopt, stats, zero): if not ifile and not code: print("Need either file or code") return -1 if ifile: code = ifile.read() else: code = '\n'.join(code.split(';')) codes = parse_and_optimize(code, noopt) absolute = GStatement(GCode('G', 90)) spindle_start = GStatement(GCode('M', 3)) codes.insert(0, absolute) codes.insert(0, spindle_start) if measure == 'metric': adjust = GCode('G', 21) else: adjust = GCode('G', 20) codes.insert(0, GStatement(adjust)) if zero: zero = GStatement(GCode('G', 0), GCode('Z', 0), GCode('X', 0), GCode('Y', 0)) codes.append(zero) codes.append(GStatement(GCode('M', 5))) if stats: print(generate_stats(codes), file=sys.stderr) if not yes: print() x = None while x != 'y': x = raw_input('Start? (y/n) ') if x == 'n': print('Aborting') return -1 cnc = CNC(device, baudrate) cnc.add_codes(*codes) cnc.onprogress, cnc.oncomplete = make_progressbar(len(cnc), 'Buffer: ') cnc.onalarm = lambda x: print('\nalarm: %s, %s' % (x, cnc.cur)) cnc.onerror = lambda x: print('\nerror: %s, %s' % (x, cnc.cur)) try: cnc.connect() cnc.send_queue() except KeyboardInterrupt: print() print('Interrupted') print('Raising position alarm') cnc.halt() return -1 return 0
def setupUi(self): self.setObjectName(_fromUtf8("MainWindow")) self.resize(603, 600) self.centralwidget = QtGui.QWidget(self) self.centralwidget.setObjectName(_fromUtf8("centralwidget")) self.horizontalLayout = QtGui.QHBoxLayout(self.centralwidget) self.horizontalLayout.setMargin(5) self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout")) self.textEdit = QtGui.QTextEdit(self.centralwidget) self.textEdit.setReadOnly(True) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(1) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.textEdit.sizePolicy().hasHeightForWidth()) self.textEdit.setSizePolicy(sizePolicy) self.textEdit.setObjectName(_fromUtf8("textEdit")) self.horizontalLayout.addWidget(self.textEdit) self.widget = GLWidget(self.centralwidget) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(1) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.widget.sizePolicy().hasHeightForWidth()) self.widget.setSizePolicy(sizePolicy) self.widget.setObjectName(_fromUtf8("widget")) self.horizontalLayout.addWidget(self.widget) spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Ignored, QtGui.QSizePolicy.Minimum) self.horizontalLayout.addItem(spacerItem) self.setCentralWidget(self.centralwidget) self.menubar = QtGui.QMenuBar(self) self.menubar.setGeometry(QtCore.QRect(0, 0, 603, 21)) self.menubar.setObjectName(_fromUtf8("menubar")) self.setMenuBar(self.menubar) self.menu_File = QtGui.QMenu(self.menubar) self.menu_File.setObjectName(_fromUtf8("menu_File")) self.menuTool = QtGui.QMenu(self.menubar) self.menuTool.setObjectName(_fromUtf8("menuTool")) self.menuPort = QtGui.QMenu(self.menubar) self.menuPort.setObjectName(_fromUtf8("menuPort")) self.menuBaud = QtGui.QMenu(self.menubar) self.menuBaud.setObjectName(_fromUtf8("menuBaud")) self.menuTool.addMenu(self.menuPort) self.menuTool.addMenu(self.menuBaud) self.menuHelp = QtGui.QMenu(self.menubar) self.menuHelp.setObjectName(_fromUtf8("menuHelp")) self.statusbar = QtGui.QStatusBar(self) self.statusbar.setObjectName(_fromUtf8("statusbar")) self.setStatusBar(self.statusbar) self.action_Open = QtGui.QAction(QtGui.QIcon('open.png'), "&Open...", self) self.action_Open.setObjectName(_fromUtf8("action_Open")) self.action_Open.triggered.connect(self.openFile) self.action_Quit = QtGui.QAction(self) self.action_Quit.setObjectName(_fromUtf8("action_Quit")) self.action_Quit.triggered.connect(self.quitapp) self.actionAbout = QtGui.QAction(self) self.actionAbout.setObjectName(_fromUtf8("actionAbout")) self.menu_File.addAction(self.action_Open) self.menu_File.addSeparator() self.menu_File.addAction(self.action_Quit) self.menuHelp.addAction(self.actionAbout) self.menubar.addAction(self.menu_File.menuAction()) self.menubar.addAction(self.menuTool.menuAction()) self.menubar.addAction(self.menuHelp.menuAction()) self.startAction = QtGui.QAction(QtGui.QIcon('play.png'), "Start", self) self.stopAction = QtGui.QAction(QtGui.QIcon('stop.png'), "Stop", self) self.rePort = QtGui.QAction("Refresh", self) self.rePort.triggered.connect(self.getPorts) self.serialCon = QtGui.QAction(QtGui.QIcon('connection.png'), "Connect", self) self.serialCon.triggered.connect(self.toggleConnection) self.toolbar = self.addToolBar("Toolbar") self.toolbar.addAction(self.action_Open) self.toolbar.addSeparator() self.toolbar.addAction(self.serialCon) self.toolbar.addAction(self.startAction) self.toolbar.addAction(self.stopAction) self.gcode = GCode() self.getPorts() self.setBaudrates() self.setBaudrate() self.setEnablePrintPanel(False) self.retranslateUi() QtCore.QMetaObject.connectSlotsByName(self)
def setUp(self): self.test_object = GCode()
class Printer(object): def __init__(self, cfg): self.cfg = cfg self.webui = None self.gcode_queue = queue.Queue(100) self.gcode_file = None self.idling = True self.pause = False self.ev_buffer = asyncio.Event() self.pid = {} self.setpoint = {} self.setpoint_fail_time = {} self.tolerance = 3 self.current_status = None self.heater_enable_mcodes = True self.heater_disable_eof = True self.ignore_endstop = False self.prepare_endswitches() self.machine_ready = False self.move = Move(self.cfg, self) self.gcode = GCode(self.cfg) dim = self.cfg.settings["num_motors"] audiodev = self.cfg.settings["sound_device"] self.current_e = 0 self.extruder_safety_timeout = 300 # FIXME self.extruder_safety_time = time.time() + self.extruder_safety_timeout for n in ["ext", "bed"]: name = n.upper() o = GPOutput("heater_" + name) s = ScaledSensor(self.cfg, name) t = Thermistor100k(s) self.pid[n] = PidController(t, o, 0.3, 0.004, 0.5) self.launch_pid(n, 20) self.loop = asyncio.get_event_loop() self.sc = StepperCluster(audiodev, dim, self.cfg, self.esw) self.sc.connect_cmd_buffer(self.move.get_output_buffer()) self.loop.add_writer(self.sc.fileno(), self.handle_sc_write) asyncio.async(self.gcode_processor()) asyncio.async(self.coro_check_machine()) def add_webui(self, webui): self.webui = webui def launch_pid(self, name, sp): self.pid[name].spawn() self.set_setpoint(name, sp) def shutdown(self): self.loop.stop() for name in self.pid: self.pid[name].set_setpoint(0) self.pid[name].shutdown() if self.sc is not None: self.sc.zero_output() self.sc.zero_output() self.sc.zero_output() self.sc.zero_output() self.sc.close() def set_setpoint(self, name, sp, report=True): if sp and sp < 10: sp = 10 elif name == "ext" and sp > 280: sp = 280 elif name == "bed" and sp > 120: sp = 120 print("Set", name, "temperature:", sp, "deg. C") self.setpoint[name] = sp self.setpoint_fail_time[name] = 0 self.pid[name].set_setpoint(sp) if report and self.webui: self.webui.queue_setpoint(name, sp) def set_zoffset(self, zoff): print("PRINTER: Set Z-offset:", zoff) self.gcode.set_zoffset(zoff) def get_temperature(self, name): return self.pid[name].get_input() def check_setpoint_immediate(self, name): temp = self.get_temperature(name) sp = self.setpoint[name] dt = abs(temp - sp) if sp < 30: # Heater off = ok dt = 0 ok = (dt < self.tolerance) return ok def check_setpoint(self, name): ok = self.check_setpoint_immediate(name) if ok: self.setpoint_fail_time[name] = 0 elif not self.setpoint_fail_time[name]: self.setpoint_fail_time[name] = time.time() + 10 return (ok or time.time() < self.setpoint_fail_time[name]) def check_setpoints(self): return self.check_setpoint("ext") and self.check_setpoint("bed") @asyncio.coroutine def coro_check_machine(self): self.tolerance = 3 wasok = True while True: res = self.check_setpoints() if res: # Hysteresis self.tolerance = 10 else: self.tolerance = 3 if not res and wasok: print("Printer not ready") elif res and not wasok: print("Printer ready") wasok = res self.machine_ready = res self.update_status() yield from asyncio.sleep(2.0) def set_position_mm(self, x, y, z, e): if self.webui: self.webui.queue_move(x, y, z, e) if e != self.current_e: self.current_e = e self.extruder_safety_time = time.time() + self.extruder_safety_timeout def printer_handler(self): ti = time.time() if self.extruder_safety_time < ti and "ext" in self.pid and \ self.setpoint["ext"] > 150: print("Extruder safety timeout hit. Lowering setpoint!") self.pid["ext"].set_setpoint(self.setpoint["ext"] - 50) @asyncio.coroutine def print_file(self, fname): if self.gcode_file is not None: return False print("Starting print:", fname) self.gcode_file = AIOFileReader(fname) asyncio.sleep(0) return True @asyncio.coroutine def wait_for_setpoints(self): while True: if self.check_setpoint_immediate("ext") and self.check_setpoint_immediate("bed"): break yield from asyncio.sleep(2) @asyncio.coroutine def start_auto(self, sp_ext, en_ext, sp_bed, en_bed, fname): if self.gcode_file is not None: return False if en_bed: # Heat up bed first self.set_setpoint("bed", sp_bed) while True: t_bed = self.get_temperature("bed") if t_bed > (sp_bed - 10): break yield from asyncio.sleep(5) if en_ext: # Bed near ok, so start hotend self.set_setpoint("ext", sp_ext) while True: t_ext = self.get_temperature("ext") if t_ext > (sp_ext - 10): break yield from asyncio.sleep(5) # Temperatures high enough to start homing self.reset() yield from self.execute_gcode("G28 X0 Y0") yield from self.wait_for_setpoints() yield from self.execute_gcode("G28 Z0") yield from self.print_file(fname) @asyncio.coroutine def execute_gcode(self, cmd): try: self.gcode_queue.put_nowait(cmd) except queue.Full: return False yield from asyncio.sleep(0) return True def _read_gcode(self): try: ret = self.gcode_queue.get_nowait() except queue.Empty: ret = "" return ret @asyncio.coroutine def _queue_move(self, obj): if self.move.buffer_ready(): self.move.process_command(obj) yield from asyncio.sleep(0) else: self.ev_buffer.clear() yield from self.ev_buffer.wait() self.move.process_command(obj) @asyncio.coroutine def gcode_processor(self): idle = True while True: if (self.gcode_file is None or self.pause) and self.gcode_queue.empty(): if not idle: yield from self._queue_move({"command":"eof"}) idle = True yield from asyncio.sleep(0.2) continue if self.gcode_file and not self.pause: if not self.machine_ready: yield from asyncio.sleep(0.2) continue l = self.gcode_file.readline() if l is None: # End of file self.gcode_file = None if self.heater_disable_eof: self.set_setpoint("ext", 0) self.set_setpoint("bed", 0) yield from self._queue_move({"command":"eof"}) self.move.reset() continue else: l = self._read_gcode() if len(l) == 0: # File reader stalled yield from self._queue_move({"command":"eof"}) self.move.reset() continue obj = self.gcode.process_line(l) if obj is None: continue # print("PRINTER GCODE:", repr(obj)) cmd = obj["command"] if cmd == "setpoint": if self.heater_enable_mcodes: self.set_setpoint(obj["type"], obj["value"]) if obj["wait"]: yield from self.wait_for_setpoints() elif cmd == "log": self.webui.queue_log(obj['type'], obj['value']) else: yield from self._queue_move(obj) idle = False self.set_idle(False) def _heater_status(self, name): ok = self.check_setpoint(name) sp = self.setpoint[name] temp = self.get_temperature(name) if sp == 0: return "off" if ok: return "ok" if sp > temp: return "low" if sp < temp: return "high" def update_status(self, force=False): if self.idling: motors = "idle" elif self.gcode_file is not None: motors = "processing" else: motors = "moving" ext = self._heater_status("ext") bed = self._heater_status("bed") status = (motors, ext, bed) if self.webui is None: return if self.current_status == status: return self.current_status = status self.webui.queue_status(*status) def set_idle(self, idle): if idle != self.idling: self.idling = idle self.update_status() def handle_sc_write(self): if not self.idling: ret = self.sc.pull_cmd_buffer() if ret == 1: self.set_idle(True) if self.move.buffer_ready() and not self.ev_buffer.is_set(): # print("PRINTER: cond notify") self.ev_buffer.set() else: self.sc.zero_output() def set_pause(self, pause): self.pause = pause print("Set pause:", repr(pause)) @asyncio.coroutine def stop(self): print("Stopping...") self.abort() yield from self.execute_gcode("G91") yield from self.execute_gcode("G1 Z5 F5000") yield from self.execute_gcode("G90") def abort(self): print("Aborting...") if self.gcode_file: self.gcode_file.close() self.gcode_file = None while not self.gcode_queue.empty(): self.gcode_queue.get_nowait() self.sc.cancel_destination() self.sc.flush_queue() # We may have interrupted a move. Make sure we know where we are... scpos = self.sc.get_position() gpos = self.move.reverse_transform(scpos) self.gcode.set_position(gpos) self.set_setpoint("ext", 0) self.set_setpoint("bed", 0) def reset(self): self.gcode.reset() self.sc.set_position([0, 0, 0, 0]) self.move.reset() self.set_position_mm(0, 0, 0, 0) def set_heater_enable_mcodes(self, value): self.heater_enable_mcodes = value def set_heater_disable_eof(self, value): self.heater_disable_eof = value def set_ignore_endstop(self, value): if self.ignore_endstop != value: self.ignore_endstop = value if value: self.disable_endswitches() else: self.enable_endswitches() def disable_endswitches(self): for e in self.esw: e.disable_exceptions() def enable_endswitches(self): for e in self.esw: e.enable_exceptions() def prepare_endswitches(self): self.esw = [] for axis in ["X", "Y", "Z"]: eswname = "endstop_" + axis self.esw.append(AsyncGPInput(eswname, self)) def gpio_event(self, name, val): print("GPIO Event from", name, "value:", val) self.sc.stop() self.sc.cancel_destination() self.sc.restart() def run(self): self.loop.run_forever()
from gcode import GCode gcode = GCode(49,19) gcode.init_file() gcode.build_file() lines = gcode.get_gcode() print('++++++++++++++++++++++++') print('lines ----->', len(lines)) # print(lines.len)
class TestGcode(unittest.TestCase): def setUp(self): self.test_object = GCode() def tearDown(self): pass def test_is_extruder_move(self): self.assertEqual( (-2.5, 1500), self.test_object.is_extruder_move(b"G1 E-2.5 F1500") ) self.assertEqual((-2.5, None), self.test_object.is_extruder_move(b"G1 E-2.5")) self.assertEqual( (-3.0, 4800), self.test_object.is_extruder_move(b"G1 E-3.00000 F4800.00000") ) self.assertEqual( (-3.0, 4800), self.test_object.is_extruder_move(b"G1 F4800.00000 E-3.00000") ) self.assertEqual( (-2.6162, 1200), self.test_object.is_extruder_move(b"G1 F1200 E-2.6162") ) self.assertEqual( None, self.test_object.is_extruder_move(b"G1 F4800.00000 E-3.00000 X0 Y0 Z0"), ) self.assertEqual((2.0, 2700), self.test_object.is_extruder_move(b"G1 F2700 E2")) def test_read_gcode_line(self): self.assertEqual( (b"G1 E5 F1500 ", b" juu"), self.test_object.read_gcode_line(b"G1 E5 F1500 ; juu"), ) self.assertEqual((None, b" juu"), self.test_object.read_gcode_line(b"; juu")) self.assertEqual( (None, b" juu ; joo"), self.test_object.read_gcode_line(b"; juu ; joo") ) self.assertEqual( (b"G1 E-3.00000 F4800.00000", None), self.test_object.read_gcode_line(b"G1 E-3.00000 F4800.00000"), ) def test_format_to_string(self): self.assertEqual( b"G1 X1 Y1 Z1 E1 F1000; test", self.test_object.format_to_string(b"G1 X1 Y1 Z1 E1 F1000", b" test"), ) self.assertEqual( b"G1 X1 Y1 Z1 E1 F1000", self.test_object.format_to_string(b"G1 X1 Y1 Z1 E1 F1000", None), ) self.assertEqual(b"; test", self.test_object.format_to_string(None, b" test")) def test_is_tool_change(self): self.assertEqual(0, self.test_object.is_tool_change(b"T0")) self.assertEqual(1, self.test_object.is_tool_change(b"T1")) self.assertEqual(1, self.test_object.is_tool_change(b"T1 ; tool 1")) def test_is_z_move(self): self.assertEqual((5.5, 1500), self.test_object.is_z_move(b"G1 Z5.500 F1500")) self.assertEqual((5.5, None), self.test_object.is_z_move(b"G1 Z5.500")) self.assertEqual( None, self.test_object.is_z_move(b"G1 X67.626 Y63.341 Z0.25 E0 F900") ) self.assertEqual((0.5, None), self.test_object.is_z_move(b"G1 Z.5")) def test_is_extrusion_move(self): self.assertEqual( (80.349, 81.849, None, -2.5, None), self.test_object.is_extrusion_move(b"G1 X80.349 Y81.849 E-2.5000"), ) self.assertEqual( None, self.test_object.is_extrusion_move(b"G1 X80.349 Y81.849 E0") ) self.assertEqual( (80.349, 81.849, None, 5, 2000), self.test_object.is_extrusion_move(b"G1 X80.349 Y81.849 E5 F2000"), ) self.assertEqual( (45.488, 56.304, 0.225, 1, 9000), self.test_object.is_extrusion_move(b"G1 X45.488 Y56.304 Z0.225 E1 F9000"), ) def test_is_head_move(self): self.assertEqual( (65.82, 76.532, 5.7, 1500), self.test_object.is_head_move(b"G1 X65.82 Y76.532 Z5.7 E0 F1500"), ) self.assertEqual( (65.82, 76.532, None, 1500), self.test_object.is_head_move(b"G1 X65.82 Y76.532 F1500"), ) self.assertEqual( (None, 76.532, None, 1500), self.test_object.is_head_move(b"G1 Y76.532 F1500"), ) self.assertEqual( (65.82, None, None, 1500), self.test_object.is_head_move(b"G1 X65.82 F1500") ) self.assertEqual( (65.82, 76.532, None, 1500), self.test_object.is_head_move(b"G1 F1500 X65.82 Y76.532"), ) self.assertEqual( (0.0, 10.0, 0.5, 5400.0), self.test_object.is_head_move(b"G0 F5400 X0.00 Y10 Z0.5"), ) self.assertEqual( (45.488, 56.304, 0.225, 9000), self.test_object.is_head_move(b"G1 X45.488 Y56.304 Z0.225 E0 F9000"), ) def test_is_lin_advance(self): self.assertEqual(100, self.test_object.is_lin_advance(b"M900 K100")) self.assertEqual(0, self.test_object.is_lin_advance(b"M900 K0")) self.assertEqual(None, self.test_object.is_lin_advance(b"M900 Ko")) self.assertEqual(0.2, self.test_object.is_lin_advance(b"M900 K0.2")) def test_gen_lin_advance(self): self.assertEqual(b"M900 K100", self.test_object.gen_lin_advance(100)) self.assertEqual(b"M900 K0.2", self.test_object.gen_lin_advance(0.2)) def test_is_pressure_advance(self): self.assertEqual( (b"0", 0.3), self.test_object.is_pressure_advance(b"M572 D0 S0.3") ) self.assertEqual( (b"0:1", 0.3), self.test_object.is_pressure_advance(b"M572 D0:1 S0.3") ) self.assertEqual( (b"0:1:2", 0.3), self.test_object.is_pressure_advance(b"M572 D0:1:2 S0.3") ) self.assertEqual( None, self.test_object.is_pressure_advance(b"M572 D0:1:a S0.3") ) def test_gen_direction_move(self): ret = self.test_object.gen_direction_move(E, 40, 3000, 0.1) expected = [b"G1 X40.000 Y0 F3000"] for r in ret: self.assertEqual(expected[0], r) def test_gen_direction_move_with_e(self): e = extruder.Extruder(0) e.coasting = 0.2 e.extrusion_width = 0.4 ret = self.test_object.gen_direction_move(W, 40, 3000, 0.2, e) expected = [b"G1 X-40.000 Y0 E1.3304 F3000"] for r in ret: self.assertEqual(expected[0], r) ret = self.test_object.gen_direction_move(S, 40, 3000, 0.1, e, last_line=True) expected = [b"G1 X0 Y-39.800 E0.6619 F3000", b"G1 X0 Y-0.200 F3000"] for r in ret: self.assertEqual(expected[0], r) expected.pop(0) def test_get_coordinates(self): self.assertEqual( (6.123233995736766e-16, 10.0), self.test_object._get_coordinates(N, 10) ) self.assertEqual( (7.0710678118654755, 7.071067811865475), self.test_object._get_coordinates(NE, 10), ) self.assertEqual((10.0, 0.0), self.test_object._get_coordinates(E, 10)) self.assertEqual( (7.071067811865474, -7.071067811865477), self.test_object._get_coordinates(SE, 10), ) self.assertEqual( (-1.8369701987210296e-15, -10.0), self.test_object._get_coordinates(S, 10) ) self.assertEqual( (-7.071067811865477, -7.071067811865475), self.test_object._get_coordinates(SW, 10), ) self.assertEqual( (-10.0, 1.2246467991473533e-15), self.test_object._get_coordinates(W, 10) ) self.assertEqual( (-7.071067811865475, 7.0710678118654755), self.test_object._get_coordinates(NW, 10), ) self.assertEqual( (8.660254037844387, 4.999999999999999), self.test_object._get_coordinates(30, 10), ) self.assertEqual( (1.7364817766693041, 9.84807753012208), self.test_object._get_coordinates(80, 10), ) self.assertEqual( (-1.736481776669303, 9.84807753012208), self.test_object._get_coordinates(100, 10), ) self.assertEqual( (-9.396926207859085, -3.4202014332566866), self.test_object._get_coordinates(200, 10), ) self.assertEqual( (9.702957262759965, -2.4192189559966786), self.test_object._get_coordinates(346, 10), ) def test_get_coordinates_by_offsets(self): self.assertEqual( (11.0, 25.0), self.test_object.get_coordinates_by_offsets(0, 10, 10, 1, 15) ) self.assertEqual( (10.738061598597138, 25.015167833783153), self.test_object.get_coordinates_by_offsets(1, 10, 10, 1, 15), ) self.assertEqual( (6.999999999999998, 25.0), self.test_object.get_coordinates_by_offsets(0, 10, 10, -3, 15), ) self.assertEqual( (4.440854075959419, 24.25117176218233), self.test_object.get_coordinates_by_offsets(10, 10, 10, -3, 15), ) self.assertEqual( (11.795967328357486, 11.33210410834607), self.test_object.get_coordinates_by_offsets(10, 10, 10, 2, 1), ) self.assertEqual( (6.408065343285028, 7.335791783307863), self.test_object.get_coordinates_by_offsets(10, 10, 10, -4, -2), ) self.assertEqual( (8.724977204643308, 5.713472632617306), self.test_object.get_coordinates_by_offsets(10, 10, 10, -2, -4), ) self.assertEqual( (12.664208216692137, 6.408065343285028), self.test_object.get_coordinates_by_offsets(10, 10, 10, 2, -4), ) def test_is_temp_nowait(self): self.assertEqual((255,), self.test_object.is_temp_nowait(b"M104 S255")) self.assertEqual( (255, 0), self.test_object.is_temp_nowait_tool(b"M104 S255 T0") ) def test_is_temp_wait(self): self.assertEqual((255,), self.test_object.is_temp_wait(b"M109 S255")) self.assertEqual((255, 0), self.test_object.is_temp_wait_tool(b"M109 S255 T0")) def test_is_fan_speed(self): self.assertEqual(255, self.test_object.is_fan_speed(b"M106 S255")) def test_opposite_dir(self): self.assertEqual(180, self.test_object.opposite_dir(E)) self.assertEqual(0, self.test_object.opposite_dir(W)) self.assertEqual(90, self.test_object.opposite_dir(S)) self.assertEqual(270, self.test_object.opposite_dir(N)) self.assertEqual(90, self.test_object.opposite_dir(E + 270)) self.assertEqual(270, self.test_object.opposite_dir(W + 270)) self.assertEqual(360, self.test_object.opposite_dir(S + 270)) self.assertEqual(180, self.test_object.opposite_dir(N + 270)) def test_rotate(self): self.assertEqual(90, self.test_object.rotate(W, 270))
class Printer(object): def __init__(self, cfg): self.cfg = cfg self.webui = None self.gcode_queue = queue.Queue(100) self.gcode_file = None self.idling = True self.pause = False self.ev_buffer = asyncio.Event() self.pid = {} self.setpoint = {} self.setpoint_fail_time = {} self.tolerance = 3 self.current_status = None self.heater_enable_mcodes = True self.heater_disable_eof = True self.ignore_endstop = False self.prepare_endswitches() self.machine_ready = False self.move = Move(self.cfg, self) self.gcode = GCode(self.cfg) dim = self.cfg.settings["num_motors"] audiodev = self.cfg.settings["sound_device"] self.current_e = 0 self.extruder_safety_timeout = 300 # FIXME self.extruder_safety_time = time.time() + self.extruder_safety_timeout for n in ["ext", "bed"]: name = n.upper() o = GPOutput("heater_" + name) s = ScaledSensor(self.cfg, name) t = Thermistor100k(s) self.pid[n] = PidController(t, o, 0.3, 0.004, 0.5) self.launch_pid(n, 20) self.loop = asyncio.get_event_loop() self.sc = StepperCluster(audiodev, dim, self.cfg, self.esw) self.sc.connect_cmd_buffer(self.move.get_output_buffer()) self.loop.add_writer(self.sc.fileno(), self.handle_sc_write) asyncio. async (self.gcode_processor()) asyncio. async (self.coro_check_machine()) def add_webui(self, webui): self.webui = webui def launch_pid(self, name, sp): self.pid[name].spawn() self.set_setpoint(name, sp) def shutdown(self): self.loop.stop() for name in self.pid: self.pid[name].set_setpoint(0) self.pid[name].shutdown() if self.sc is not None: self.sc.zero_output() self.sc.zero_output() self.sc.zero_output() self.sc.zero_output() self.sc.close() def set_setpoint(self, name, sp, report=True): if sp and sp < 10: sp = 10 elif name == "ext" and sp > 280: sp = 280 elif name == "bed" and sp > 120: sp = 120 print("Set", name, "temperature:", sp, "deg. C") self.setpoint[name] = sp self.setpoint_fail_time[name] = 0 self.pid[name].set_setpoint(sp) if report and self.webui: self.webui.queue_setpoint(name, sp) def set_zoffset(self, zoff): print("PRINTER: Set Z-offset:", zoff) self.gcode.set_zoffset(zoff) def get_temperature(self, name): return self.pid[name].get_input() def check_setpoint_immediate(self, name): temp = self.get_temperature(name) sp = self.setpoint[name] dt = abs(temp - sp) if sp < 30: # Heater off = ok dt = 0 ok = (dt < self.tolerance) return ok def check_setpoint(self, name): ok = self.check_setpoint_immediate(name) if ok: self.setpoint_fail_time[name] = 0 elif not self.setpoint_fail_time[name]: self.setpoint_fail_time[name] = time.time() + 10 return (ok or time.time() < self.setpoint_fail_time[name]) def check_setpoints(self): return self.check_setpoint("ext") and self.check_setpoint("bed") @asyncio.coroutine def coro_check_machine(self): self.tolerance = 3 wasok = True while True: res = self.check_setpoints() if res: # Hysteresis self.tolerance = 10 else: self.tolerance = 3 if not res and wasok: print("Printer not ready") elif res and not wasok: print("Printer ready") wasok = res self.machine_ready = res self.update_status() yield from asyncio.sleep(2.0) def set_position_mm(self, x, y, z, e): if self.webui: self.webui.queue_move(x, y, z, e) if e != self.current_e: self.current_e = e self.extruder_safety_time = time.time( ) + self.extruder_safety_timeout def printer_handler(self): ti = time.time() if self.extruder_safety_time < ti and "ext" in self.pid and \ self.setpoint["ext"] > 150: print("Extruder safety timeout hit. Lowering setpoint!") self.pid["ext"].set_setpoint(self.setpoint["ext"] - 50) @asyncio.coroutine def print_file(self, fname): if self.gcode_file is not None: return False print("Starting print:", fname) self.gcode_file = AIOFileReader(fname) asyncio.sleep(0) return True @asyncio.coroutine def wait_for_setpoints(self): while True: if self.check_setpoint_immediate( "ext") and self.check_setpoint_immediate("bed"): break yield from asyncio.sleep(2) @asyncio.coroutine def start_auto(self, sp_ext, en_ext, sp_bed, en_bed, fname): if self.gcode_file is not None: return False if en_bed: # Heat up bed first self.set_setpoint("bed", sp_bed) while True: t_bed = self.get_temperature("bed") if t_bed > (sp_bed - 10): break yield from asyncio.sleep(5) if en_ext: # Bed near ok, so start hotend self.set_setpoint("ext", sp_ext) while True: t_ext = self.get_temperature("ext") if t_ext > (sp_ext - 10): break yield from asyncio.sleep(5) # Temperatures high enough to start homing self.reset() yield from self.execute_gcode("G28 X0 Y0") yield from self.wait_for_setpoints() yield from self.execute_gcode("G28 Z0") yield from self.print_file(fname) @asyncio.coroutine def execute_gcode(self, cmd): try: self.gcode_queue.put_nowait(cmd) except queue.Full: return False yield from asyncio.sleep(0) return True def _read_gcode(self): try: ret = self.gcode_queue.get_nowait() except queue.Empty: ret = "" return ret @asyncio.coroutine def _queue_move(self, obj): if self.move.buffer_ready(): self.move.process_command(obj) yield from asyncio.sleep(0) else: self.ev_buffer.clear() yield from self.ev_buffer.wait() self.move.process_command(obj) @asyncio.coroutine def gcode_processor(self): idle = True while True: if (self.gcode_file is None or self.pause) and self.gcode_queue.empty(): if not idle: yield from self._queue_move({"command": "eof"}) idle = True yield from asyncio.sleep(0.2) continue if self.gcode_file and not self.pause: if not self.machine_ready: yield from asyncio.sleep(0.2) continue l = self.gcode_file.readline() if l is None: # End of file self.gcode_file = None if self.heater_disable_eof: self.set_setpoint("ext", 0) self.set_setpoint("bed", 0) yield from self._queue_move({"command": "eof"}) self.move.reset() continue else: l = self._read_gcode() if len(l) == 0: # File reader stalled yield from self._queue_move({"command": "eof"}) self.move.reset() continue obj = self.gcode.process_line(l) if obj is None: continue # print("PRINTER GCODE:", repr(obj)) cmd = obj["command"] if cmd == "setpoint": if self.heater_enable_mcodes: self.set_setpoint(obj["type"], obj["value"]) if obj["wait"]: yield from self.wait_for_setpoints() elif cmd == "log": self.webui.queue_log(obj['type'], obj['value']) else: yield from self._queue_move(obj) idle = False self.set_idle(False) def _heater_status(self, name): ok = self.check_setpoint(name) sp = self.setpoint[name] temp = self.get_temperature(name) if sp == 0: return "off" if ok: return "ok" if sp > temp: return "low" if sp < temp: return "high" def update_status(self, force=False): if self.idling: motors = "idle" elif self.gcode_file is not None: motors = "processing" else: motors = "moving" ext = self._heater_status("ext") bed = self._heater_status("bed") status = (motors, ext, bed) if self.webui is None: return if self.current_status == status: return self.current_status = status self.webui.queue_status(*status) def set_idle(self, idle): if idle != self.idling: self.idling = idle self.update_status() def handle_sc_write(self): if not self.idling: ret = self.sc.pull_cmd_buffer() if ret == 1: self.set_idle(True) if self.move.buffer_ready() and not self.ev_buffer.is_set(): # print("PRINTER: cond notify") self.ev_buffer.set() else: self.sc.zero_output() def set_pause(self, pause): self.pause = pause print("Set pause:", repr(pause)) @asyncio.coroutine def stop(self): print("Stopping...") self.abort() yield from self.execute_gcode("G91") yield from self.execute_gcode("G1 Z5 F5000") yield from self.execute_gcode("G90") def abort(self): print("Aborting...") if self.gcode_file: self.gcode_file.close() self.gcode_file = None while not self.gcode_queue.empty(): self.gcode_queue.get_nowait() self.sc.cancel_destination() self.sc.flush_queue() # We may have interrupted a move. Make sure we know where we are... scpos = self.sc.get_position() gpos = self.move.reverse_transform(scpos) self.gcode.set_position(gpos) self.set_setpoint("ext", 0) self.set_setpoint("bed", 0) def reset(self): self.gcode.reset() self.sc.set_position([0, 0, 0, 0]) self.move.reset() self.set_position_mm(0, 0, 0, 0) def set_heater_enable_mcodes(self, value): self.heater_enable_mcodes = value def set_heater_disable_eof(self, value): self.heater_disable_eof = value def set_ignore_endstop(self, value): if self.ignore_endstop != value: self.ignore_endstop = value if value: self.disable_endswitches() else: self.enable_endswitches() def disable_endswitches(self): for e in self.esw: e.disable_exceptions() def enable_endswitches(self): for e in self.esw: e.enable_exceptions() def prepare_endswitches(self): self.esw = [] for axis in ["X", "Y", "Z"]: eswname = "endstop_" + axis self.esw.append(AsyncGPInput(eswname, self)) def gpio_event(self, name, val): print("GPIO Event from", name, "value:", val) self.sc.stop() self.sc.cancel_destination() self.sc.restart() def run(self): self.loop.run_forever()
class MainWindow(QWidget): def __init__(self, appinst, profilecon, settingscon, *args, **kwargs): super().__init__(*args, **kwargs) self.app = appinst self.profilecon = profilecon # connector class instance for reading/writing profile settings self.settingscon = settingscon self.gcode = None self.machine = None self.backgroundTask = None self.postBackgroundTask = None self.coord_plot_items = list( ) # list of all plot items added to the coord plot self.mainlayout = QVBoxLayout(self) self.mainlayout.setContentsMargins(0, 0, 0, 0) self.toolBar = QToolBar() self.toolBar.setStyleSheet("""QToolBar {background-color: white; border-top: 1px solid black}""" ) self.mainlayout.addWidget(self.toolBar) self.add_toolbar_action("./res/folder.svg", "Open", self.open_file_dialog) self.add_toolbar_action("./res/x-square.svg", "Close", self.close_file) self.add_toolbar_action("./res/save.svg", "Export", self.export) self.toolBar.addSeparator() self.add_toolbar_action("./res/sliders.svg", "Settings", self.open_settings_dialog) self.add_toolbar_action("./res/play.svg", "Simulate", self.start_simulation) self.toolBar.addSeparator() self.add_toolbar_action("./res/maximize.svg", "Fit to View", self.fit_plot_to_window) self.add_toolbar_action("./res/maximize-2.svg", "Reset View", self.reset_plot_view) self.toolBar.addSeparator() self.profileSelector = QComboBox() for name in self.profilecon.list_profiles(): self.profileSelector.addItem(name) self.profileSelector.setCurrentText( self.settingscon.get_value("Current_Profile")) self.profilecon.select_profile( self.settingscon.get_value("Current_Profile")) self.toolBar.addWidget(self.profileSelector) self.profileSelector.currentTextChanged.connect( self.selected_profile_changed) divider = QWidget() divider.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.toolBar.addWidget(divider) self.add_toolbar_action("./res/info.svg", "About", self.open_about_dialog) self.contentLayout = QHBoxLayout() self.contentLayout.setContentsMargins(10, 10, 10, 10) self.mainlayout.addLayout(self.contentLayout) self.layerSlider = QSlider() self.layerSlider.setMinimum(0) self.layerSlider.setValue(0) self.layerSlider.setDisabled(True) self.layerSlider.valueChanged.connect(self.show_layer) self.contentLayout.addWidget(self.layerSlider) self.coordPlot = PlotWidget() self.coordPlot.setAspectLocked(True) # self.coordPlot.setLimits(xMin=0, yMin=0) self.configure_plot( ) # is done in a seperate funciton because values need to be updated after settings are changed self.contentLayout.addWidget(self.coordPlot) self.sidebarlayout = QVBoxLayout() self.contentLayout.addLayout(self.sidebarlayout) self.sidebarheader = QLabel("Options") self.sidebarheader.setFixedSize(300, 50) self.sidebarlayout.addWidget(self.sidebarheader) def configure_plot(self): self.coordPlot.invertX( self.profilecon.get_value("invert_x") ) # needs to be done before setting the axis ranges because self.coordPlot.invertY( self.profilecon.get_value("invert_y") ) # inverting does not update the viewbox, but setting the range does self.coordPlot.setXRange(self.profilecon.get_value("bed_min_x"), self.profilecon.get_value("bed_max_x")) self.coordPlot.setYRange(self.profilecon.get_value("bed_min_y"), self.profilecon.get_value("bed_max_y")) def selected_profile_changed(self, new_profile): # select the new profile in the settings connector and update the ui accordingly self.profilecon.select_profile(new_profile) self.settingscon.set_value("Current_Profile", new_profile) # remember selected profile self.settingscon.save_to_file() self.configure_plot() def add_toolbar_action(self, icon, text, function): # wrapper function for adding a toolbar button and connecting it to trigger a function open_icon = QIcon(icon) action = self.toolBar.addAction(open_icon, text) action.triggered.connect(function) def finish_background_task(self): # function is called when a background task finishes if self.postBackgroundTask: # run cleanup task (i.e. ui update); runs on main ui thread! self.postBackgroundTask() # reset variables self.postBackgroundTask = None self.backgroundTask = None def run_in_background(self, task, after=None, args=None): # wrapper function for creating and starting a thread to run a function in the background # arguments can be passed to the function in the thread and a cleanup function can be specified # which is run on the main ui thread when the background task is finished self.backgroundTask = BackgroundTask(task) if args: self.backgroundTask.set_arguments(args) self.backgroundTask.finished.connect(self.finish_background_task) self.postBackgroundTask = after self.backgroundTask.start() def open_file_dialog(self): # in case a file is open already, close it properly first if self.machine: ret = self.close_file() if not ret: # user canceled closing of current file; can't open new one return # open dialog for selecting a gcode file to be loaded dialog = QFileDialog(self) dialog.setFileMode(QFileDialog.ExistingFile) filters = ["G-code (*.gcode)", "Any files (*)"] dialog.setNameFilters(filters) dialog.selectNameFilter(filters[0]) dialog.setViewMode(QFileDialog.Detail) filename = None if dialog.exec_(): filename = dialog.selectedFiles() if filename: self.run_in_background(self.load_data, after=self.show_layer, args=filename) def open_settings_dialog(self): # open a dialog with settings dialog = SettingsDialog(self, self.profilecon) dialog.exec() # update settings self.configure_plot() def open_about_dialog(self): # open the about dialog dialog = QDialog() dialog.setWindowTitle("About...") layout = QVBoxLayout() dialog.setLayout(layout) text = QLabel(strings.about) layout.addWidget(text) dialog.exec() def close_file(self): # close the current gcode file, discard all data # Before, ask for user confirmation cfmsgbox = QMessageBox() cfmsgbox.setWindowTitle("Close file?") cfmsgbox.setText( "Are you sure you want to close the current file and discard all unsaved data?" ) cfmsgbox.setStandardButtons(QMessageBox.Yes | QMessageBox.No) cfmsgbox.setDefaultButton(QMessageBox.No) ret = cfmsgbox.exec() if ret == QMessageBox.Yes: for item in self.coord_plot_items: self.coordPlot.removeItem(item) self.machine = None self.gcode = None # TODO: fix: this will not terminate a running background process return True return False def export(self): pass def start_simulation(self): pass def fit_plot_to_window(self): x, y = self.machine.get_path_coordinates( layer_number=self.layerSlider.value()) self.coordPlot.setRange(xRange=(min(x), max(x)), yRange=(min(y), max(y))) def reset_plot_view(self): self.coordPlot.setXRange(self.profilecon.get_value("bed_min_x"), self.profilecon.get_value("bed_max_x")) self.coordPlot.setYRange(self.profilecon.get_value("bed_min_y"), self.profilecon.get_value("bed_max_y")) def load_data(self, filename): # initalizes a virtual machine from the gcode in the file given # all path data for this gcode is calculated; this is a cpu intensive task! self.gcode = GCode() self.gcode.load_file(filename) self.machine = Machine(self.gcode, self.profilecon) self.machine.create_path() # set the layer sliders maximum to represent the given amount of layers and enable the slider self.layerSlider.setMaximum(len(self.machine.layers) - 1) self.layerSlider.setEnabled(True) def show_layer(self): # plot path for the layer selected by the layer slider x, y = self.machine.get_path_coordinates( layer_number=self.layerSlider.value()) pltitm = self.coordPlot.plot(x, y, clear=True) self.coord_plot_items.append(pltitm)
class Slic3rEngineRunner(QObject): ''' This is just connector to console version of Slic3r software first version ''' step_increased = pyqtSignal(int) filament_info = pyqtSignal(str) finished = pyqtSignal() send_message = pyqtSignal(str) send_gcodedata = pyqtSignal(GCode) support_parameters = ["support_material_angle", "support_material_buildplate_only", "support_material_contact_distance", "support_material_enforce_layers", "support_material_extruder", "support_material_extrusion_width", "support_material_interface_extruder", "support_material_interface_layers", "support_material_interface_spacing", "support_material_interface_speed", "support_material_pattern", "support_material_spacing", "support_material_threshold", "support_material_with_sheath", "support_material_xy_spacing"] multimaterial_spec_parameters = ["bridge_fan_speed", "cooling", "deretract_speed", "disable_fan_first_layers", "extrusion_multiplier", "fan_always_on", "fan_below_layer_time", "filament_density", "filament_diameter", "filament_max_volumetric_speed", "filament_type", "filament_soluble", "first_layer_bed_temperature", "first_layer_temperature", "max_fan_speed", "max_layer_height", "min_fan_speed", "min_layer_height", "min_print_speed", "nozzle_diameter", "retract_before_travel", "retract_before_wipe", "retract_layer_change", "retract_length", "retract_length_toolchange", "retract_lift", "retract_lift_above", "retract_lift_below", "retract_restart_extra", "retract_restart_extra_toolchange", "retract_speed", "slowdown_below_layer_time", "temperature", "wipe"] def __init__(self, controller): super(Slic3rEngineRunner, self).__init__() self.is_running = True self.controller = controller self.gcode = GCode(self.controller.app_config.tmp_place + 'out.gcode', self.controller, None, None) system_platform = platform.system() if system_platform in ['Linux']: self.slicer_place = [self.controller.app_config.local_path + "tools/Slic3r-Lite/bin/slic3r"] elif system_platform in ['Darwin']: self.slicer_place = [self.controller.app_config.local_path + "tools/Slic3r-Lite/Slic3r.app/Contents/MacOS/Slic3r"] elif system_platform in ['Windows']: self.slicer_place = ['tools\\Slic3r-Lite\\slic3r-noconsole.exe'] else: self.slicer_place = ['slic3r'] #print(self.slicer_place) self.step_max = 9 self.step = 0 def translate_dictionary(self, old, update): translation_table = [ ['fill_density', 'infill', self.percent_transform], ['brim_width', 'brim', self.brim_transform], #['support_material', 'support', self.boolean_transform] ['support_material', 'support_on_off', self.support1_transform], ['support_material_buildplate_only', 'support_build_plate', self.support2_transform], ['overhangs', 'overhangs', self.support3_transform], ['support_material_extruder', 'support_material_extruder', self.support4_transform], ['support_material_interface_extruder', 'support_material_interface_extruder', self.str_transform], ['wipe_tower', 'is_wipe_tower', self.str_transform], ['wipe_tower_per_color_wipe', 'wipe_size_y', self.str_transform], ['wipe_tower_width', 'wipe_size_x', self.str_transform], ['wipe_tower_x', 'wipe_pos_x', self.str_transform], ['wipe_tower_y', 'wipe_pos_y', self.str_transform], ['single_extruder_multi_material', 'is_multimat', self.str_transform] ] for i in translation_table: old[i[0]] = i[2](update[i[1]]) return old def percent_transform(self, in_value): return "%s" % str(in_value) + '%' def brim_transform(self, in_value): return "%s" % str(int(in_value)*10) def support1_transform(self, in_value): print("Support transform 1: " + str(in_value)) if in_value == 0: #None return "0" elif in_value >= 1: #Build plate only return "1" return "0" def support2_transform(self, in_value): print("Support transform 2: " + str(in_value)) if in_value == 0: #None return "0" elif in_value == 1: #Build plate only return "1" elif in_value == 2: #Everywhere return "0" elif in_value == 3: #Build plate only with interface return "1" elif in_value == 4: #Everywhere with interface return "0" else: return "0" return "0" def support3_transform(self, in_value): print("Support transform 3: " + str(in_value)) if in_value == 0: #None return "0" elif in_value >= 1: #other support options return "1" else: return "1" return "0" def support4_transform(self, in_value): print("Support transform 4: " + str(in_value)) #support material extruder [a, b] = in_value if b == 0: #None return "0" elif b == 1: #Build plate only return self.str_transform(a) elif b == 2: #Everywhere return self.str_transform(a) elif b > 2: #Build plate only return "0" return "0" def str_transform(self, in_value): return "%s" % str(in_value) def list_to_str(self, lst): return ','.join(str(e) for e in lst) def save_configuration(self, filename): actual_printing_data = self.controller.get_actual_printing_data() for i in actual_printing_data: if i in ['brim', 'support_on_off'] and actual_printing_data[i]==True: self.step_max+=1 #material_printing_data = self.controller.get_printing_parameters_for_material_quality(actual_printing_data['material'], actual_printing_data['quality']) material_printing_data = self.controller.printing_parameters.get_actual_settings(self.controller.get_actual_printer(), self.controller.settings['printer_type'], actual_printing_data['material'], actual_printing_data['quality'], self) #print("All settings: " + str(material_printing_data)) new_parameters = self.translate_dictionary(material_printing_data, actual_printing_data) new_config = configparser.RawConfigParser() new_config.add_section('settings') #new_config.set('settings', i, new_parameters) for i in new_parameters: if type(new_parameters[i]) == list: new_config.set('settings', i, self.list_to_str(new_parameters[i])) else: new_config.set('settings', i, new_parameters[i]) #write ini file with open(filename, 'w') as ini_file: fake_file = io.StringIO() new_config.write(fake_file) ini_file.write(fake_file.getvalue()[11:]) print("saved") def slice(self): self.save_configuration(self.controller.app_config.tmp_place + 'prusacontrol.ini') self.process = subprocess.Popen( self.slicer_place + [self.controller.app_config.tmp_place + 'tmp.prusa', '--load', self.controller.app_config.tmp_place + 'prusacontrol.ini', '--output', self.controller.app_config.tmp_place + 'out.gcode', '--dont-arrange'], stdout=subprocess.PIPE) #if self.controller.is_multimaterial() and not self.controller.is_single_material_mode(): # self.process = subprocess.Popen( # self.slicer_place + [self.controller.app_config.tmp_place + 'tmp.prusa', '--load', # self.controller.app_config.tmp_place + 'prusacontrol.ini', '--output', # self.controller.app_config.tmp_place + 'out.gcode', '--dont-arrange'], # stdout=subprocess.PIPE) #else: # self.process = subprocess.Popen(self.slicer_place + [self.controller.app_config.tmp_place + 'tmp.stl', '--load', # self.controller.app_config.tmp_place + 'prusacontrol.ini', '--output', # self.controller.app_config.tmp_place + 'out.gcode', '--dont-arrange'], # stdout=subprocess.PIPE) self.check_progress() def kill(self): self.process.kill() def check_progress(self): self.step = 1 while self.is_running is True: self.step+=1 if self.process.returncode == -signal.SIGSEGV: self.send_message.emit("Slic3r engine crash") break line = str(self.process.stdout.readline(), 'utf-8') parsed_line = line.rsplit() print(parsed_line) if not line: continue if 'Done.' in parsed_line[0]: self.step_increased.emit(95) self.send_message.emit("Generating G-code preview") self.gcode.read_in_realtime() self.send_gcodedata.emit(self.gcode) self.send_message.emit("") elif 'Filament' in parsed_line[0] and 'required:' in parsed_line[1]: filament_str = str(parsed_line[2] + ' ' + parsed_line[3]) print(filament_str) self.filament_info.emit(filament_str) self.finished.emit() #self.step_increased.emit(100) break else: text = line.rsplit()[1:] if text[0] == 'Exporting': text = text[:2] self.send_message.emit(" ".join(text)) self.step_increased.emit(int((self.step / 12.) * 100)) def end(self): self.end_callback() def get_version(self): version_process = subprocess.Popen(self.slicer_place + ["--version"], stdout=subprocess.PIPE) version_info = str(version_process.stdout.readline(), 'utf-8') return version_info
from serialComunicator import Device from gcode import GCode from time import sleep from picamera import PiCamera gcode = GCode(2,2) gcode.init_file() gcode.build_file() camera = PiCamera() def start(): arduino = Device(port='/dev/ttyUSB0', baud_rate=115200) arduino.connect_device() sleep(2) arduino.send_command('$X',timeout=4) sleep(2) arduino.load_grlb_config() sleep(5) camera.start_preview() lines = gcode.get_gcode() do_gcode_lines(lines, arduino) def finish(): camera.stop_preview() camera.close() def take_picture(c, delay): sleep(delay)
class Slic3rEngineRunner(QObject): ''' This is just connector to console version of Slic3r software first version ''' step_increased = pyqtSignal(int) filament_info = pyqtSignal(str) finished = pyqtSignal() send_message = pyqtSignal(str) send_gcodedata = pyqtSignal(GCode) #support parameters for soluble materials, list of parameters which will be used from soluble materials support_parameters = [ "support_material_angle", "support_material_buildplate_only", "support_material_contact_distance", "support_material_enforce_layers", "support_material_extruder", "support_material_extrusion_width", "support_material_interface_extruder", "support_material_interface_layers", "support_material_interface_spacing", "support_material_interface_speed", "support_material_pattern", "support_material_spacing", "support_material_threshold", "support_material_with_sheath", "support_material_xy_spacing", "support_material_synchronize_layers" ] multimaterial_spec_parameters = [ "bridge_fan_speed", "cooling", "deretract_speed", "disable_fan_first_layers", "extrusion_multiplier", "fan_always_on", "fan_below_layer_time", "filament_density", "filament_diameter", "filament_max_volumetric_speed", "filament_type", "filament_soluble", "first_layer_bed_temperature", "first_layer_temperature", "max_fan_speed", "max_layer_height", "min_fan_speed", "min_layer_height", "min_print_speed", "nozzle_diameter", "retract_before_travel", "retract_before_wipe", "retract_layer_change", "retract_length", "retract_length_toolchange", "retract_lift", "retract_lift_above", "retract_lift_below", "retract_restart_extra", "retract_restart_extra_toolchange", "retract_speed", "slowdown_below_layer_time", "temperature", "wipe" ] def __init__(self, controller): super(Slic3rEngineRunner, self).__init__() self.is_running = True self.controller = controller self.gcode = GCode(self.controller.app_config.tmp_place + 'out.gcode', self.controller, None, None) system_platform = platform.system() if system_platform in ['Linux']: self.slicer_place = [ self.controller.app_config.local_path + "tools/Slic3r-Lite/bin/slic3r" ] elif system_platform in ['Darwin']: self.slicer_place = [ self.controller.app_config.local_path + "tools/Slic3r-Lite/Slic3r.app/Contents/MacOS/Slic3r" ] elif system_platform in ['Windows']: self.slicer_place = ['tools\\Slic3r-Lite\\slic3r-noconsole.exe'] else: self.slicer_place = ['slic3r'] #print(self.slicer_place) self.step_max = 9 self.step = 0 def translate_dictionary(self, old, update): translation_table = [ ['fill_density', 'infill', self.percent_transform], ['brim_width', 'brim', self.brim_transform], #['support_material', 'support', self.boolean_transform] ['support_material', 'support_on_off', self.support1_transform], [ 'support_material_buildplate_only', 'support_build_plate', self.support2_transform ], ['overhangs', 'overhangs', self.support3_transform], [ 'support_material_extruder', 'support_material_extruder', self.support4_transform ], [ 'support_material_interface_extruder', 'support_material_interface_extruder', self.str_transform ], ['wipe_tower', 'is_wipe_tower', self.str_transform], ['wipe_tower_per_color_wipe', 'wipe_size_y', self.str_transform], ['wipe_tower_width', 'wipe_size_x', self.str_transform], ['wipe_tower_x', 'wipe_pos_x', self.str_transform], ['wipe_tower_y', 'wipe_pos_y', self.str_transform], [ 'single_extruder_multi_material', 'is_multimat', self.str_transform ] ] for i in translation_table: old[i[0]] = i[2](update[i[1]]) return old def percent_transform(self, in_value): return "%s" % str(in_value) + '%' def brim_transform(self, in_value): return "%s" % str(int(in_value) * 10) def support1_transform(self, in_value): print("Support transform 1: " + str(in_value)) if in_value == 0: #None return "0" elif in_value >= 1: #Build plate only return "1" return "0" def support2_transform(self, in_value): print("Support transform 2: " + str(in_value)) if in_value == 0: #None return "0" elif in_value == 1: #Build plate only return "1" elif in_value == 2: #Everywhere return "0" elif in_value == 3: #Build plate only with interface return "1" elif in_value == 4: #Everywhere with interface return "0" else: return "0" return "0" def support3_transform(self, in_value): print("Support transform 3: " + str(in_value)) if in_value == 0: #None return "0" elif in_value >= 1: #other support options return "1" else: return "1" return "0" def support4_transform(self, in_value): print("Support transform 4: " + str(in_value)) #support material extruder [a, b] = in_value if b == 0: #None return "0" elif b == 1: #Build plate only return self.str_transform(a) elif b == 2: #Everywhere return self.str_transform(a) elif b > 2: #Build plate only return "0" return "0" def str_transform(self, in_value): return "%s" % str(in_value) def list_to_str(self, lst): return ','.join(str(e) for e in lst) def save_configuration(self, filename): actual_printing_data = self.controller.get_actual_printing_data() for i in actual_printing_data: if i in ['brim', 'support_on_off' ] and actual_printing_data[i] == True: self.step_max += 1 #material_printing_data = self.controller.get_printing_parameters_for_material_quality(actual_printing_data['material'], actual_printing_data['quality']) material_printing_data = self.controller.printing_parameters.get_actual_settings( self.controller.get_actual_printer(), self.controller.settings['printer_type'], actual_printing_data['material'], actual_printing_data['quality'], self) #print("All settings: " + str(material_printing_data)) new_parameters = self.translate_dictionary(material_printing_data, actual_printing_data) new_config = configparser.RawConfigParser() new_config.add_section('settings') #new_config.set('settings', i, new_parameters) for i in new_parameters: if type(new_parameters[i]) == list: new_config.set('settings', i, self.list_to_str(new_parameters[i])) else: new_config.set('settings', i, new_parameters[i]) #write ini file with open(filename, 'w') as ini_file: fake_file = io.StringIO() new_config.write(fake_file) ini_file.write(fake_file.getvalue()[11:]) print("saved") def slice(self): self.save_configuration(self.controller.app_config.tmp_place + 'prusacontrol.ini') self.process = subprocess.Popen(self.slicer_place + [ self.controller.app_config.tmp_place + 'tmp.prusa', '--load', self.controller.app_config.tmp_place + 'prusacontrol.ini', '--output', self.controller.app_config.tmp_place + 'out.gcode', '--dont-arrange' ], stdout=subprocess.PIPE) #if self.controller.is_multimaterial() and not self.controller.is_single_material_mode(): # self.process = subprocess.Popen( # self.slicer_place + [self.controller.app_config.tmp_place + 'tmp.prusa', '--load', # self.controller.app_config.tmp_place + 'prusacontrol.ini', '--output', # self.controller.app_config.tmp_place + 'out.gcode', '--dont-arrange'], # stdout=subprocess.PIPE) #else: # self.process = subprocess.Popen(self.slicer_place + [self.controller.app_config.tmp_place + 'tmp.stl', '--load', # self.controller.app_config.tmp_place + 'prusacontrol.ini', '--output', # self.controller.app_config.tmp_place + 'out.gcode', '--dont-arrange'], # stdout=subprocess.PIPE) self.check_progress() def kill(self): self.process.kill() def check_progress(self): #self.gcode.set_running_variable(self.is_running) self.step = 16 step_coef = 40. / (len(self.controller.scene.get_models(False)) * 7.) while self.is_running is True: self.step += (1. * step_coef) if self.process.returncode == -signal.SIGSEGV: self.send_message.emit("Slic3r engine crash") break line = str(self.process.stdout.readline(), 'utf-8') parsed_line = line.rsplit() print(parsed_line) if not line: continue if 'Done.' in parsed_line[0]: self.step_increased.emit(75) self.send_message.emit("Generating G-code preview") if self.gcode.read_in_realtime(True, self.set_gcode_progressbar): self.send_gcodedata.emit(self.gcode) else: self.send_message.emit("") return self.send_message.emit("") elif 'Filament' in parsed_line[0] and 'required:' in parsed_line[1]: filament_str = str(parsed_line[2] + ' ' + parsed_line[3]) self.filament_info.emit(filament_str) self.finished.emit() #self.step_increased.emit(100) break else: text = line.rsplit()[1:] if text[0] == 'Exporting': text = text[:2] self.send_message.emit(" ".join(text)) self.step_increased.emit(int(self.step)) def set_gcode_progressbar(self, value): start = 75 final = int(((value + 0.001) / 100.) * 25.) self.step_increased.emit(start + final) def end(self): self.end_callback() def get_version(self): version_process = subprocess.Popen(self.slicer_place + ["--version"], stdout=subprocess.PIPE) version_info = str(version_process.stdout.readline(), 'utf-8') return version_info
def test_invalid_commands(self): # none of the lines should load with default parameters # therefore every line needs to be checked seperatly with open("gc_invalid_commands.gcode", "r") as fobj: linenumber = 0 for line in fobj.readlines(): gcode = GCode() with self.assertRaises(ValueError, msg="GCode Line {}: \n {}".format(linenumber, line)): gcode.parse_line(line, linenumber) # load and ignore errors, keep lines gcode = GCode(ignore_invalid=True, keep_invalid=True) gcode.load_file("gc_invalid_commands.gcode") self.assertEqual(len(gcode._lines), 4) # four commands should be loaded for i in range(4): self.assertEqual(gcode._lines[i].is_valid(), False) # no command should be valid # load and ignore errors, discard lines gcode = GCode(ignore_invalid=True, keep_invalid=False) gcode.load_file("gc_invalid_commands.gcode") self.assertEqual(len(gcode._lines), 0) # no commands should be loaded
class Slic3rEngineRunner(QObject): ''' This is just connector to console version of Slic3r software first version ''' step_increased = pyqtSignal(int) filament_info = pyqtSignal(str) finished = pyqtSignal() send_message = pyqtSignal(str) send_gcodedata = pyqtSignal(GCode) def __init__(self, controller): super(Slic3rEngineRunner, self).__init__() self.is_running = True self.controller = controller self.gcode = GCode(self.controller.app_config.tmp_place + 'out.gcode', self.controller, None, None) system_platform = platform.system() if system_platform in ['Linux']: self.slicer_place = ['/home/tibor/dev/Slic3r/bin/slic3r'] #self.slicer_place = './tools/Slic3r-Lite/slic3r' elif system_platform in ['Darwin']: self.slicer_place = [ self.controller.app_config.local_path + "tools/Slic3r-Lite/Slic3r.app/Contents/MacOS/Slic3r" ] elif system_platform in ['Windows']: self.slicer_place = ['tools\\Slic3r-Lite\\slic3r-noconsole.exe'] else: self.slicer_place = ['slic3r'] #print(self.slicer_place) self.step_max = 9 self.step = 0 def translate_dictionary(self, old, update): translation_table = [ ['fill_density', 'infill', self.percent_transform], ['brim_width', 'brim', self.boolean_transform], #['support_material', 'support', self.boolean_transform] ['support_material', 'support_on_off', self.support1_transform], [ 'support_material_buildplate_only', 'support_build_plate', self.support2_transform ], ['overhangs', 'overhangs', self.support3_transform] ] for i in translation_table: old[i[0]] = i[2](update[i[1]]) return old def percent_transform(self, in_value): return "%s" % str(in_value) + '%' def boolean_transform(self, in_value): return "%s" % str(int(in_value) * 10) def support1_transform(self, in_value): if in_value == 0: #None return "0" elif in_value == 1: #Build plate only return "1" elif in_value == 2: #Everywhere return "1" else: return "0" return "0" def support2_transform(self, in_value): if in_value == 0: #None return "0" elif in_value == 1: #Build plate only return "1" elif in_value == 2: #Everywhere return "0" else: return "0" return "0" def support3_transform(self, in_value): if in_value == 0: #None return "0" elif in_value == 1: #Build plate only return "1" elif in_value == 2: #Everywhere return "1" else: return "1" return "0" def save_configuration(self, filename): actual_printing_data = self.controller.get_actual_printing_data() for i in actual_printing_data: if i in ['brim', 'support_on_off' ] and actual_printing_data[i] == True: self.step_max += 1 #material_printing_data = self.controller.get_printing_parameters_for_material_quality(actual_printing_data['material'], actual_printing_data['quality']) material_printing_data = self.controller.printing_parameters.get_actual_settings( self.controller.actual_printer, self.controller.settings['printer_type'], actual_printing_data['material'], actual_printing_data['quality']) #print("All settings: " + str(material_printing_data)) new_parameters = self.translate_dictionary(material_printing_data, actual_printing_data) new_config = configparser.RawConfigParser() new_config.add_section('settings') #new_config.set('settings', i, new_parameters) for i in new_parameters: new_config.set('settings', i, new_parameters[i]) #write ini file with open(filename, 'w') as ini_file: fake_file = io.StringIO() new_config.write(fake_file) ini_file.write(fake_file.getvalue()[11:]) def slice(self): self.save_configuration(self.controller.app_config.tmp_place + 'prusacontrol.ini') self.process = subprocess.Popen(self.slicer_place + [ self.controller.app_config.tmp_place + 'tmp.stl', '--load', self.controller.app_config.tmp_place + 'prusacontrol.ini', '--output', self.controller.app_config.tmp_place + 'out.gcode', '--dont-arrange' ], stdout=subprocess.PIPE) self.check_progress() def kill(self): self.process.kill() def check_progress(self): self.step = 1 while self.is_running is True: self.step += 1 if self.process.returncode == -signal.SIGSEGV: self.send_message.emit("Slic3r engine crash") break line = str(self.process.stdout.readline(), 'utf-8') parsed_line = line.rsplit() print(parsed_line) if not line: continue if 'Done.' in parsed_line[0]: self.step_increased.emit(95) self.send_message.emit("Generating G-code preview") self.gcode.read_in_realtime() self.send_gcodedata.emit(self.gcode) self.send_message.emit("") elif 'Filament' in parsed_line[0] and 'required:' in parsed_line[1]: filament_str = str(parsed_line[2] + ' ' + parsed_line[3]) print(filament_str) self.filament_info.emit(filament_str) self.finished.emit() #self.step_increased.emit(100) break else: text = line.rsplit()[1:] if text[0] == 'Exporting': text = text[:2] self.send_message.emit(" ".join(text)) self.step_increased.emit(int((self.step / 12.) * 100)) def end(self): self.end_callback() def get_version(self): version_process = subprocess.Popen(self.slicer_place + ["--version"], stdout=subprocess.PIPE) version_info = str(version_process.stdout.readline(), 'utf-8') return version_info