pync.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. """
  2. # Python Gcode wrapper
  3. #
  4. # Copyright (c) 2010-2018 Michael Buesch <m@bues.ch>
  5. #
  6. # Licensed under the GNU General Public License version 2
  7. # or (at your option) any later version.
  8. #
  9. # Example usage:
  10. from pync import *
  11. G54 | G0(0, 0, 2) | S(1000) | F(100) | M3
  12. for (x, y) in ((0, 0), (10, 10), (20, 20)):
  13. G0(x, y)
  14. G1(Z=-20)
  15. G0(Z=2)
  16. """
  17. import sys
  18. import six
  19. import atexit
  20. import datetime
  21. from math import *
  22. class __PyNC_State(object):
  23. def __init__(self):
  24. self.reset()
  25. def reset(self):
  26. self.lineBuffer = []
  27. def dump(self, fd=sys.stdout):
  28. code = "\n".join(str(l) for l in self.lineBuffer)
  29. fd.write(code + "\n")
  30. fd.flush()
  31. pync = __PyNC_State()
  32. class _Line_Metaclass(type):
  33. """Metaclass for class Line.
  34. """
  35. def __meta_merge(self, other):
  36. return self()._merge(other)
  37. __add__ = __meta_merge # a + b
  38. __sub__ = __meta_merge # a - b
  39. __mul__ = __meta_merge # a * b
  40. __lshift__ = __meta_merge # a << b
  41. __rshift__ = __meta_merge # a >> b
  42. __and__ = __meta_merge # a & b
  43. __xor__ = __meta_merge # a ^ b
  44. __or__ = __meta_merge # a | b
  45. @six.add_metaclass(_Line_Metaclass)
  46. class Line(object):
  47. """A generic Gcode line.
  48. """
  49. def __init__(self, code=""):
  50. self.code = code
  51. pync.lineBuffer.append(self)
  52. def _merge(self, add):
  53. if not add:
  54. return self
  55. if isinstance(add, str):
  56. self.code += " " + add
  57. else:
  58. if not isinstance(add, Line):
  59. add = add()
  60. assert(isinstance(add, Line))
  61. self.code += " " + add.code
  62. pync.lineBuffer.remove(add)
  63. return self
  64. __add__ = _merge # a + b
  65. __sub__ = _merge # a - b
  66. __mul__ = _merge # a * b
  67. __lshift__ = _merge # a << b
  68. __rshift__ = _merge # a >> b
  69. __and__ = _merge # a & b
  70. __xor__ = _merge # a ^ b
  71. __or__ = _merge # a | b
  72. def __repr__(self):
  73. return self.code
  74. @staticmethod
  75. def makecoordnum(coord):
  76. if equal(coord, int(coord)):
  77. return str(int(coord))
  78. return ("%f" % float(coord)).rstrip("0")
  79. @staticmethod
  80. def makecoord(axis, coord):
  81. return axis + Line.makecoordnum(coord)
  82. @staticmethod
  83. def makegcodenum(number):
  84. if isinstance(number, float):
  85. return "%.1f" % number
  86. return "%d" % number
  87. @staticmethod
  88. def makegcode(code, number):
  89. return code + Line.makegcodenum(number)
  90. @staticmethod
  91. def checkargs(kwargs, allowedList):
  92. for arg in kwargs.keys():
  93. if arg not in allowedList:
  94. raise TypeError("Got an unexpected keyword "
  95. "argument '%s'" % arg)
  96. raw = Line
  97. class Comment(Line):
  98. "A human readable comment"
  99. def __init__(self, text="", space=" "):
  100. text = str(text).replace("\r", " ").split("\n")
  101. Line.__init__(self, Comment.__format(text[0], space))
  102. if len(text) > 1: # Remaining lines, if any.
  103. Comment("\n".join(text[1:]), space)
  104. @staticmethod
  105. def __format(text, space):
  106. if text:
  107. return "(%s%s%s)" % (space, text, space)
  108. return ""
  109. c = Comment
  110. class Print(Comment):
  111. "Print a message"
  112. def __init__(self, text):
  113. Comment.__init__(self, "PRINT," + text, space="")
  114. p = Print
  115. class Debug(Comment):
  116. "Print a debugging message"
  117. def __init__(self, text):
  118. Comment.__init__(self, "DEBUG," + text, space="")
  119. d = Debug
  120. class LogOpen(Comment):
  121. "Open a log file"
  122. def __init__(self, filename):
  123. Comment.__init__(self, "LOGOPEN," + filename, space="")
  124. class LogClose(Comment):
  125. "Close the log file"
  126. def __init__(self):
  127. Comment.__init__(self, "LOGCLOSE", space="")
  128. class Log(Comment):
  129. "Write text to the log file"
  130. def __init__(self, text):
  131. Comment.__init__(self, "LOG," + text, space="")
  132. l = Log
  133. class G(Line):
  134. "Generic G-code"
  135. def __init__(self, number, X=None, Y=None, Z=None,
  136. A=None, B=None, C=None,
  137. I=None, J=None, K=None,
  138. U=None, V=None, W=None,
  139. P=None, Q=None, R=None,
  140. L=None, D=None,
  141. **kwargs):
  142. Line.__init__(self, self.makegcode("G", number))
  143. for paramStr in ("X", "Y", "Z",
  144. "A", "B", "C",
  145. "I", "J", "K",
  146. "U", "V", "W",
  147. "P", "Q", "R",
  148. "L", "D"):
  149. param = eval(paramStr) # Fetch __init__ argument
  150. if param is None:
  151. param = kwargs.get(paramStr.lower())
  152. if param is not None:
  153. self << Line.makecoord(paramStr, param)
  154. class G0(G):
  155. "G0 => rapid move"
  156. def __init__(self, X=None, Y=None, Z=None,
  157. A=None, B=None, C=None,
  158. U=None, V=None, W=None,
  159. **kwargs):
  160. self.checkargs(kwargs, {"x", "y", "z",
  161. "a", "b", "c",
  162. "u", "v", "w"})
  163. G.__init__(self, 0, X=X, Y=Y, Z=Z,
  164. A=A, B=B, C=C,
  165. U=U, V=V, W=W,
  166. **kwargs)
  167. rapid = G0
  168. class G1(G):
  169. "G1 => move"
  170. def __init__(self, X=None, Y=None, Z=None,
  171. A=None, B=None, C=None,
  172. U=None, V=None, W=None,
  173. **kwargs):
  174. self.checkargs(kwargs, {"x", "y", "z",
  175. "a", "b", "c",
  176. "u", "v", "w"})
  177. G.__init__(self, 1, X=X, Y=Y, Z=Z,
  178. A=A, B=B, C=C,
  179. U=U, V=V, W=W,
  180. **kwargs)
  181. move = G1
  182. class G2(G):
  183. "G2 => clockwise arc move"
  184. def __init__(self, X=None, Y=None, Z=None,
  185. I=None, J=None, K=None,
  186. R=None,
  187. **kwargs):
  188. self.checkargs(kwargs, {"x", "y", "z",
  189. "i", "j", "k",
  190. "r"})
  191. G.__init__(self, 2, X=X, Y=Y, Z=Z,
  192. I=I, J=J, K=K,
  193. R=R,
  194. **kwargs)
  195. arcCw = G2
  196. class G3(G):
  197. "G3 => counterclockwise arc move"
  198. def __init__(self, X=None, Y=None, Z=None,
  199. I=None, J=None, K=None,
  200. R=None,
  201. **kwargs):
  202. self.checkargs(kwargs, {"x", "y", "z",
  203. "i", "j", "k",
  204. "r"})
  205. G.__init__(self, 3, X=X, Y=Y, Z=Z,
  206. I=I, J=J, K=K,
  207. R=R,
  208. **kwargs)
  209. arcCcw = G3
  210. class G10L1(G):
  211. "G10 L1 => Set tool table"
  212. def __init__(self, P=None, R=None, X=None,
  213. Z=None, Q=None,
  214. **kwargs):
  215. self.checkargs(kwargs, {"p", "r", "x",
  216. "z", "q"})
  217. G.__init__(self, 10, L=1, P=P, R=R,
  218. X=X, Z=Z, Q=Q,
  219. **kwargs)
  220. class G10L2(G):
  221. "G10 L2 => Set coordinate system"
  222. def __init__(self, P=None, X=None, Y=None, Z=None,
  223. A=None, B=None, C=None,
  224. U=None, V=None, W=None,
  225. **kwargs):
  226. self.checkargs(kwargs, {"p", "x", "y", "z",
  227. "a", "b", "c",
  228. "u", "v", "w"})
  229. G.__init__(self, 10, L=2, P=P,
  230. X=X, Y=Y, Z=Z,
  231. A=A, B=B, C=C,
  232. U=U, V=V, W=W,
  233. **kwargs)
  234. class G64(G):
  235. "G64 => Path control mode"
  236. def __init__(self, P=None, **kwargs):
  237. self.checkargs(kwargs, {"p"})
  238. G.__init__(self, 64, P=P, **kwargs)
  239. class G41(G):
  240. "G41 => Cutter radius compensation - left"
  241. def __init__(self, D=None, **kwargs):
  242. self.checkargs(kwargs, {"d"})
  243. G.__init__(self, 41, D=D, **kwargs)
  244. class G42(G):
  245. "G42 => Cutter radius compensation - right"
  246. def __init__(self, D=None, **kwargs):
  247. self.checkargs(kwargs, {"d"})
  248. G.__init__(self, 42, D=D, **kwargs)
  249. class G41_1(G):
  250. "G41_1 => Dynamic cutter radius compensation - left"
  251. def __init__(self, D=None, L=None, **kwargs):
  252. self.checkargs(kwargs, {"d", "l"})
  253. G.__init__(self, 41.1, D=D, L=L, **kwargs)
  254. class G42_1(G):
  255. "G42_1 => Dynamic cutter radius compensation - right"
  256. def __init__(self, D=None, L=None, **kwargs):
  257. self.checkargs(kwargs, {"d", "l"})
  258. G.__init__(self, 42.1, D=D, L=L, **kwargs)
  259. class M(Line):
  260. "Generic M-code"
  261. def __init__(self, number):
  262. Line.__init__(self, self.makegcode("M", number))
  263. class S(Line):
  264. "Set spindle speed"
  265. def __init__(self, number):
  266. Line.__init__(self, self.makegcode("S", number))
  267. class F(Line):
  268. "Set feed rate"
  269. def __init__(self, number):
  270. Line.__init__(self, self.makegcode("F", number))
  271. class T(Line):
  272. "Set tool"
  273. def __init__(self, number):
  274. Line.__init__(self, self.makegcode("T", number))
  275. def __genTrivial(code, number):
  276. template = """
  277. class %s%s(%s):
  278. def __init__(self):
  279. %s.__init__(self, %s)
  280. """
  281. numStr = Line.makegcodenum(number)
  282. className = numStr.replace(".", "_")
  283. return template % (code, className, code,
  284. code, numStr)
  285. # Declare trivial G codes
  286. for __num in (17, 18, 19, 17.1, 18.1, 19.1,
  287. 20, 21,
  288. 28, 28.1,
  289. 30, 30.1,
  290. 40,
  291. 49,
  292. 53, 54, 55, 56, 57, 58, 59, 59.1, 59.2, 59.3,
  293. 61, 61.1,
  294. 80,
  295. 90, 91, 90.1, 91.1,
  296. 93, 94, 95):
  297. exec(__genTrivial("G", __num))
  298. # Declare trivial M codes
  299. for __num in range(0, 200):
  300. exec(__genTrivial("M", __num))
  301. def equal(a, b):
  302. "Test a and b for equalness. Returns bool. Also works for float."
  303. if isinstance(a, float) or isinstance(b, float):
  304. return abs(float(a) - float(b)) < 0.00001
  305. return a == b
  306. eq = equal
  307. ne = lambda a, b: not equal(a, b)
  308. ge = lambda a, b: equal(a, b) or a > b
  309. le = lambda a, b: equal(a, b) or a < b
  310. lt = lambda a, b: not equal(a, b) and a < b
  311. gt = lambda a, b: not equal(a, b) and a > b
  312. def __prologue():
  313. "Generate the file prologue"
  314. pync.reset()
  315. Line("%")
  316. Comment("This file is generated by PYNC gcode wrapper from '%s'." % sys.argv[0])
  317. Comment(str(datetime.datetime.today()))
  318. Comment()
  319. G54 | G17 | G40 | G49 | G80 | G90 | G91_1 | G94 | G61 | G21
  320. def __epilogue():
  321. "Generate an M30 (program end) and dump the code."
  322. M30()
  323. Line("%")
  324. pync.dump()
  325. # Generate prologue and epilogue
  326. __prologue()
  327. atexit.register(__epilogue)
  328. # Clean up namespace
  329. del sys, six, atexit, datetime
  330. del __prologue, __epilogue,
  331. del __PyNC_State
  332. del _Line_Metaclass,
  333. del __num, __genTrivial