First pass almost working. Need to not have the STL show up.
This commit is contained in:
parent
3cadbbeb85
commit
711d18d9ad
11 changed files with 256 additions and 73 deletions
57
octoprint/cura/__init__.py
Normal file
57
octoprint/cura/__init__.py
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
__author__ = "Ross Hendrickson savorywatt"
|
||||
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
|
||||
|
||||
import logging
|
||||
|
||||
from octoprint.settings import settings
|
||||
|
||||
class CuraFactory(object):
|
||||
|
||||
@staticmethod
|
||||
def create_slicer(path=None):
|
||||
if path:
|
||||
return CuraEngine(path)
|
||||
current_settings = settings(init=True)
|
||||
path = current_settings.get(["curaEngine", "path"])
|
||||
|
||||
return CuraEngine(path)
|
||||
|
||||
|
||||
class CuraEngine(object):
|
||||
|
||||
def __init__(self, cura_path):
|
||||
|
||||
if not cura_path:
|
||||
raise Exception("Unable to create CuraEngine - no path specified")
|
||||
|
||||
self.cura_path = cura_path
|
||||
|
||||
logging.info('CuraEngine Created')
|
||||
|
||||
|
||||
def process_file(self, config, gcode, file_path, call_back, call_back_args):
|
||||
"""Wraps around the main.cpp processFile method.
|
||||
:param config: :class: `string` :path to a cura config file:
|
||||
:param gcode: :class: `string :path to write out the gcode generated:
|
||||
:param file_path: :class: `string :path to the STL to be sliced:
|
||||
:note: This will spawn a thread to handle the subprocess call and allow
|
||||
us to be able to have a call back
|
||||
"""
|
||||
import threading
|
||||
|
||||
def start_thread(call_back, call_back_args, call_args):
|
||||
import subprocess
|
||||
logging.info("Starting SubProcess in Thread")
|
||||
logging.info("Subprocess args: %s" % str(call_args))
|
||||
process = subprocess.call(call_args)
|
||||
call_back(*call_back_args)
|
||||
logging.info("Slicing call back complete")
|
||||
|
||||
args = [self.cura_path, '-s', config, '-o', gcode, file_path]
|
||||
logging.info('CuraEngine args:%s' % str(args))
|
||||
|
||||
thread = threading.Thread(target=start_thread, args=(call_back,
|
||||
call_back_args, args))
|
||||
|
||||
thread.start()
|
||||
logging.info('CuraEngine Slicing File:%s' % file_path)
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
__author__ = "Ross Hendrickson savorywatt"
|
||||
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
|
||||
|
||||
import logging
|
||||
import subprocess
|
||||
|
||||
APPNAME="OctoPrint"
|
||||
|
||||
|
||||
class CuraFactory(object):
|
||||
|
||||
CURA_PATH = '/home/rosshendrickson/workspaces/opensource/CuraEngine/CuraEngine'
|
||||
|
||||
@staticmethod
|
||||
def create_slicer(path=None):
|
||||
|
||||
if path:
|
||||
return CuraEngine(path)
|
||||
else:
|
||||
return CuraEngine(CuraFactory.CURA_PATH)
|
||||
|
||||
|
||||
|
||||
class CuraEngine(object):
|
||||
|
||||
def __init__(self, cura_path):
|
||||
|
||||
|
||||
self.cura_path = cura_path
|
||||
|
||||
logging.info('CuraEngine Created')
|
||||
|
||||
|
||||
def process_file(self, config, gcode, file_path):
|
||||
"""Wraps around the main.cpp processFile method.
|
||||
|
||||
:param config: :class: `string` :path to a cura config file:
|
||||
:param gcode: :class: `string :path to write out the gcode generated:
|
||||
:param file_path: :class: `string :path to the STL to be sliced:
|
||||
|
||||
:note This just uses subprocess at the moment.
|
||||
"""
|
||||
|
||||
args = [self.cura_path, '-s', config, '-o', gcode, file_path]
|
||||
logging.info('CuraEngine args:%s' % str(args))
|
||||
|
||||
process = subprocess.call(args)
|
||||
logging.info('CuraEngine Exit:%s' % str(process))
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
|
||||
|
||||
import unittest
|
||||
from mock import patch
|
||||
|
||||
from cura import CuraFactory
|
||||
from cura import CuraEngine
|
||||
from octoprint.cura import CuraFactory
|
||||
from octoprint.cura import CuraEngine
|
||||
|
||||
class CuraFactoryTestCase(unittest.TestCase):
|
||||
|
||||
|
|
@ -15,14 +16,19 @@ class CuraFactoryTestCase(unittest.TestCase):
|
|||
|
||||
self.assertEqual(fake_path, result.cura_path)
|
||||
|
||||
def test_cura_engine_process_file(self):
|
||||
|
||||
cura_engine = CuraFactory.create_slicer()
|
||||
@patch('threading.Thread')
|
||||
def test_cura_engine_process_file(self, thread):
|
||||
path = 'rosshendrickson/workspaces/opensource/CuraEngine/'
|
||||
|
||||
cura_engine = CuraFactory.create_slicer(path)
|
||||
file_path = './cura/tests/test.stl'
|
||||
config_path = './cura/tests/config'
|
||||
gcode_filename= './cura/tests/output.gcode'
|
||||
|
||||
file_path = './cura/tests/test.stl'
|
||||
config_path = './cura/tests/config'
|
||||
gcode_filename= './cura/tests/output.gcode'
|
||||
|
||||
cura_engine.process_file(config_path, gcode_filename, file_path)
|
||||
args = [path, '-s', config_path, '-o', file_path]
|
||||
|
||||
cura_engine.process_file(config_path, gcode_filename, file_path)
|
||||
|
||||
self.assertTrue(thread.called)
|
||||
|
||||
|
|
|
|||
0
octoprint/filemanager/__init__.py
Normal file
0
octoprint/filemanager/__init__.py
Normal file
6
octoprint/filemanager/types.py
Normal file
6
octoprint/filemanager/types.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
|
||||
class FileTypes(object):
|
||||
|
||||
STL = "stl"
|
||||
GCODE = "gcode"
|
||||
|
|
@ -114,18 +114,55 @@ class GcodeManager:
|
|||
#~~ file handling
|
||||
|
||||
def addFile(self, file):
|
||||
if file:
|
||||
absolutePath = self.getAbsolutePath(file.filename, mustExist=False)
|
||||
if absolutePath is not None:
|
||||
if file.filename in self._metadata.keys():
|
||||
# delete existing metadata entry, since the file is going to get overwritten
|
||||
del self._metadata[file.filename]
|
||||
self._metadataDirty = True
|
||||
self._saveMetadata()
|
||||
file.save(absolutePath)
|
||||
self._metadataAnalyzer.addFileToQueue(os.path.basename(absolutePath))
|
||||
return self._getBasicFilename(absolutePath)
|
||||
return None
|
||||
from octoprint.filemanager.types import FileTypes
|
||||
if not file:
|
||||
return None
|
||||
|
||||
absolutePath = self.getAbsolutePath(file.filename, mustExist=False)
|
||||
logging.info("Abs Path %s" % absolutePath)
|
||||
if absolutePath is None:
|
||||
return None
|
||||
|
||||
file.save(absolutePath)
|
||||
fileType = file.filename.rsplit(".", 1)[1]
|
||||
|
||||
if not fileType:
|
||||
return None
|
||||
|
||||
if fileType == FileTypes.GCODE:
|
||||
return self.processGcode(file.filename, absolutePath)
|
||||
|
||||
if fileType == FileTypes.STL:
|
||||
return self.processSTL(file.filename, absolutePath)
|
||||
|
||||
def processSTL(self, filename, absolutePath):
|
||||
|
||||
from octoprint.cura import CuraFactory
|
||||
|
||||
callBack = self.processGcode
|
||||
gcodeFileName = util.genGcodeFileName(filename)
|
||||
gcodePath = util.genGcodeFileName(absolutePath)
|
||||
|
||||
callBackArgs = [gcodeFileName, gcodePath]
|
||||
|
||||
curaEngine = CuraFactory.create_slicer()
|
||||
current_settings = settings()
|
||||
|
||||
config = current_settings.get(["curaEngine", "config"])
|
||||
|
||||
curaEngine.process_file(
|
||||
config, gcodePath, absolutePath, callBack, callBackArgs)
|
||||
|
||||
return self._getBasicFilename(absolutePath)
|
||||
|
||||
def processGcode(self, filename, absolutePath):
|
||||
if filename in self._metadata.keys():
|
||||
# delete existing metadata entry, since the file is going to get overwritten
|
||||
del self._metadata[filename]
|
||||
self._metadataDirty = True
|
||||
self._saveMetadata()
|
||||
self._metadataAnalyzer.addFileToQueue(os.path.basename(absolutePath))
|
||||
return self._getBasicFilename(absolutePath)
|
||||
|
||||
def removeFile(self, filename):
|
||||
filename = self._getBasicFilename(filename)
|
||||
|
|
@ -139,11 +176,11 @@ class GcodeManager:
|
|||
|
||||
def getAbsolutePath(self, filename, mustExist=True):
|
||||
"""
|
||||
Returns the absolute path of the given filename in the gcode upload folder.
|
||||
Returns the absolute path of the given filename in the correct upload folder.
|
||||
|
||||
Ensures that the file
|
||||
<ul>
|
||||
<li>has the extension ".gcode"</li>
|
||||
<li>has the extension ".gcode" or ".stl"</li>
|
||||
<li>exists and is a file (not a directory) if "mustExist" is set to True</li>
|
||||
</ul>
|
||||
|
||||
|
|
@ -153,9 +190,10 @@ class GcodeManager:
|
|||
"""
|
||||
filename = self._getBasicFilename(filename)
|
||||
|
||||
if not util.isAllowedFile(filename, set(["gcode"])):
|
||||
if not util.isAllowedFile(filename, set(["gcode", "stl"])):
|
||||
return None
|
||||
|
||||
# TODO: detect which type of file and add in the extra folder portion
|
||||
secure = os.path.join(self._uploadFolder, secure_filename(self._getBasicFilename(filename)))
|
||||
if mustExist and (not os.path.exists(secure) or not os.path.isfile(secure)):
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -308,6 +308,7 @@ def readGcodeFile(filename):
|
|||
@login_required
|
||||
def uploadGcodeFile():
|
||||
filename = None
|
||||
logging.info(str(request.files.keys()))
|
||||
if "gcode_file" in request.files.keys():
|
||||
file = request.files["gcode_file"]
|
||||
filename = gcodeManager.addFile(file)
|
||||
|
|
|
|||
0
octoprint/tests/__init__.py
Normal file
0
octoprint/tests/__init__.py
Normal file
97
octoprint/tests/test_file_manager.py
Normal file
97
octoprint/tests/test_file_manager.py
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
import unittest
|
||||
from mock import Mock
|
||||
from mock import patch
|
||||
|
||||
import logging
|
||||
|
||||
class FileManipulationTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
||||
from octoprint.settings import settings
|
||||
from octoprint.gcodefiles import GcodeManager
|
||||
self.settings = settings(True)
|
||||
self.manager = GcodeManager()
|
||||
|
||||
self.filenames = []
|
||||
|
||||
def tearDown(self):
|
||||
|
||||
for filename in self.filenames:
|
||||
self.manager.removeFile(filename)
|
||||
|
||||
logging.info("REMOVED %s filenames" % str(len(self.filenames)))
|
||||
|
||||
@patch('octoprint.cura.CuraEngine.process_file')
|
||||
def test_add_stl_file(self, process):
|
||||
|
||||
fake = Mock()
|
||||
fake.filename = "test_stl.stl"
|
||||
self.filenames.append(fake.filename)
|
||||
fake.__getitem__ = "SOMETHING"
|
||||
|
||||
result = self.manager.addFile(fake)
|
||||
|
||||
logging.info("RESULT:%s" % str(result))
|
||||
|
||||
self.assertTrue(fake.filename == result)
|
||||
|
||||
self.assertTrue(process.called)
|
||||
|
||||
def test_add_gcode_file(self):
|
||||
fake = Mock()
|
||||
fake.filename = "test_stl.gcode"
|
||||
self.filenames.append(fake.filename)
|
||||
fake.__getitem__ = "SOMETHING"
|
||||
|
||||
result = self.manager.addFile(fake)
|
||||
|
||||
logging.info("RESULT:%s" % str(result))
|
||||
|
||||
self.assertTrue(fake.filename == result)
|
||||
|
||||
|
||||
class FileUtilTestCase(unittest.TestCase):
|
||||
|
||||
def test_isGcode(self):
|
||||
|
||||
from octoprint.util import isGcodeFileName
|
||||
|
||||
filename = "/asdj/wefasdf/junk.stl"
|
||||
|
||||
result = isGcodeFileName(filename)
|
||||
|
||||
self.assertFalse(result)
|
||||
|
||||
filename = "/asdj/wefasdf/junk.gcode"
|
||||
|
||||
result = isGcodeFileName(filename)
|
||||
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_isSTLFileName(self):
|
||||
|
||||
from octoprint.util import isSTLFileName
|
||||
filename = "/asdj/wefasdf/junk.stl"
|
||||
|
||||
result = isSTLFileName(filename)
|
||||
|
||||
self.assertTrue(result)
|
||||
|
||||
filename = "/asdj/wefasdf/junk.gcode"
|
||||
|
||||
result = isSTLFileName(filename)
|
||||
|
||||
self.assertFalse(result)
|
||||
|
||||
def test_genGcodeFileName(self):
|
||||
|
||||
from octoprint.util import genGcodeFileName
|
||||
|
||||
filename = "test.stl"
|
||||
|
||||
expected = "test.gcode"
|
||||
|
||||
result = genGcodeFileName(filename)
|
||||
|
||||
self.assertEqual(result, expected)
|
||||
9
octoprint/tests/test_server.py
Normal file
9
octoprint/tests/test_server.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import unittest
|
||||
|
||||
|
||||
class FileManipulationTestCase(unittest.TestCase):
|
||||
|
||||
|
||||
def test_simple(self):
|
||||
|
||||
self.assertTrue(True)
|
||||
|
|
@ -43,4 +43,21 @@ def getClass(name):
|
|||
return m
|
||||
|
||||
def matchesGcode(line, gcode):
|
||||
return re.search("^\s*%s\D" % gcode, line, re.I)
|
||||
return re.search("^\s*%s\D" % gcode, line, re.I)
|
||||
|
||||
def isGcodeFileName(filename):
|
||||
return "." in filename and filename.rsplit(".", 1)[1] in ["gcode", "GCODE"]
|
||||
|
||||
def isSTLFileName(filename):
|
||||
return "." in filename and filename.rsplit(".", 1)[1] in ["stl", "STL"]
|
||||
|
||||
def genGcodeFileName(filename):
|
||||
|
||||
if not filename:
|
||||
return None
|
||||
|
||||
if "." not in filename:
|
||||
return filename + ".gcode"
|
||||
|
||||
return filename.replace('.stl', '.gcode')
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue