-
Notifications
You must be signed in to change notification settings - Fork 49
Adding a new machine
There are two steps to creating a new machine
- Make a UI panel (which should be a class that inherits from
OutputPanel
) - Make a Python module (which should be stored in
koko/cam/machines
and be registered inkoko/cam/machines/__init__.py
)
We'll discuss the module first, then go through an example machine UI panel.
Each machine is defined by a .py
file in koko/cam/machines
. Each machine module needs a few module-level parameters:
Parameter | Description |
---|---|
INPUT | Type of panel that feeds into this machine (Python class) |
PANEL | This machine's panel (Python class, should inherit from OutputPanel ) |
DEFAULTS | List of tuples, each of which contains a defaults name and a dictionary containing default settings for various types of panels |
Let's look at the module-level variables in koko/cam/machines/universal.py
as an example:
INPUT = ContourPanel
PANEL = UniversalOutput
DEFAULTS = [
('<None>', {}),
('Cardboard',
{CadImgPanel: [('res',5)],
ContourPanel: [('diameter', 0.25)],
UniversalOutput: [('power', 25), ('speed', 75),
('rate', 500), ('xmin', 0), ('ymin', 0)]
}
)
]
This is saying that our machine's UI panel class is UniversalOutput
and it should be preceeded by a panel of type ContourPanel
. The DEFAULTS
array defines two sets of defaults:
- The first, titled
<None>
, defines no defaults - The second, named
Cardboard
, defines a default resolution for aCadImgPanel
, a default diameter for aContourPanel
, and a set of defaults for theUniversalOutput
panel itself.
When a set of defaults is chosen, kokopelli
looks through all of the panels that make out the current workflow. If any of those panels appear as keys in the defaults dictionary, it sets the given parameters to the provided values.
Each machine needs a UI panel derived from OutputPanel
. The OutputPanel
class handles many UI aspects automatically. When creating a derived class, you don't actually need to use wx
at all (unless you're doing something unusual). Let's look at koko/cam/machines/universal.py
as an example.
First, note that we save a class-level variable named extension
. This is used by the Save file
dialog as the extension of our output files.
Here is the constructor for UniversalOutput
, our example class:
def __init__(self, parent):
OutputPanel.__init__(self, parent)
self.construct('Universal laser cutter', [
('2D power (%)', 'power', int, lambda f: 0 <= f <= 100),
('Speed (%)', 'speed', int, lambda f: 0 <= f <= 100),
('Rate','rate', int, lambda f: f > 0),
('xmin (mm)', 'xmin', float, lambda f: f >= 0),
('ymin (mm)', 'ymin', float, lambda f: f >= 0)
], start=True)
We construct the parent class, then call self.construct
with a set of arguments. The first argument is the title of this panel; the second is a list of (label, name, type, checker) tuples, and the last is a boolean determining whether there should be a Start
button on the UI panel (if false, there's only a Save
button).
Next, let's look at the run
function:
def run(self, paths):
''' Convert the path from the previous panel into an epilog
laser file (with .epi suffix).
'''
values = self.get_values()
if not values: return False
koko.FRAME.status = 'Converting to .uni file'
self.file = tempfile.NamedTemporaryFile(suffix=self.extension)
job_name = koko.APP.filename if koko.APP.filename else 'untitled'
self.file.write("Z") # initialize
self.file.write("t%s~;" % job_name) # job name
self.file.write("IN;DF;PS0;DT~") # initialize
self.file.write("s%c" % (values['rate']/10)) # ppi
speed_hi = chr((648*values['speed']) / 256)
speed_lo = chr((648*values['speed']) % 256)
self.file.write("v%c%c" % (speed_hi, speed_lo))
power_hi = chr((320*values['power']) / 256)
power_lo = chr((320*values['power']) % 256)
self.file.write("p%c%c" % (power_hi, power_lo))
self.file.write("a%c" % 2) # air assist on high
scale = 1000/25.4 # The laser's tick is 1000 DPI
xoffset = values['xmin']*scale
yoffset = values['ymin']*scale
xy = lambda x,y: (xoffset + scale*x, yoffset + scale*y)
for path in paths:
self.file.write("PU;PA%d,%d;PD;" % xy(*path.points[0][0:2]))
for pt in path.points[1:]:
self.file.write("PA%d,%d;" % xy(*pt[0:2]))
self.file.write("\n")
self.file.write("e") # end of file
self.file.flush()
koko.FRAME.status = ''
return True
This function is called when we try to generate the machine-specific path file. The input argument paths
is the output from the previous panel in the pipeline (which is a ContourPanel
for this machine).
The first function call, self.get_values()
, returns a dictionary mapping names (e.g. rate
, xmin
, ymin
defined in the self.construct
call) to values.
We create a temporary file named self.file
, then write out the machine-specific commands, calling self.file.flush()
when finished.
Finally, we return True
to indicate success.