1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
|
#!/usr/bin/env python3
# Qtvcp
#
# Copyright (c) 2017 Chris Morley <chrisinnanaimo@hotmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
###############################################################################
import os
import hashlib
from qtvcp.core import Status, Info, Action
# Set up logging
from . import logger
STATUS = Status()
INFO = Info()
ACTION = Action()
LOG = logger.getLogger(__name__)
# Force the log level for this module
# LOG.setLevel(logger.INFO) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL
KEYWORDS = ['T', 'P', 'X', 'Y', 'Z', 'A', 'B', 'C', 'U', 'V', 'W', 'D', 'I', 'J', 'Q', ';']
class _TStat(object):
def __init__(self):
# only initialize once for all instances
if self.__class__._instanceNum >= 1:
return
self.__class__._instanceNum += 1
self._delay = 0
self._hash_code = None
self.NUM = 0
self.POCKET = 1
self.X = 2
self.Y = 3
self.Z = 4
self.A = 5
self.B = 6
self.C = 7
self.U = 8
self.V = 9
self.W = 10
self.DIAMETER = 11
self.FRONTANGLE = 12
self.BACKANGLE = 13
self.ORIENTATION = 14
self.COMMENTS = 15
self.hash_check = None
self.toolfile = INFO.TOOL_FILE_PATH
self.tool_wear_info = None
self.current_tool_num = -1
self.toolinfo = None
STATUS.connect('periodic', self.periodic_check)
STATUS.connect('forced-update', lambda o: self.emit_update())
def GET_TOOL_INFO(self, toolnum):
self.current_tool_num = int(toolnum)
self._reload()
return self.toolinfo
def GET_TOOL_ARRAY(self):
info = self.GET_TOOL_MODELS()
return info[0] + info[1]
def GET_TOOL_MODELS(self):
return self._reload()
def SAVE_TOOLFILE(self, array):
return self._save(array)
def ADD_TOOL(self, newtool=[-99, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, 'New Tool']):
info = self.GET_TOOL_MODELS()
info[0].insert(0, newtool)
return self._save(info[0] + info[1])
def DELETE_TOOLS(self, tools):
ta = self.GET_TOOL_ARRAY()
if type(tools) == int:
tools = [tools]
return self._save(ta, tools)
# [0] = tool number
# [1] = pocket number
# [2] = X offset
# [3] = Y offset
# [4] = Z offset
# [5] = A offset
# [6] = B offset
# [7] = C offset
# [8] = U offset
# [9] = V offset
# [10] = W offset
# [11] = tool diameter
# [12] = frontangle
# [13] = backangle
# [14] = tool orientation
# [15] = tool comments
# Reload the tool file into the array model and update tool_info
def _reload(self):
if self.toolfile is None or not os.path.exists(self.toolfile):
LOG.debug("Toolfile does not exist' {}".format(self.toolfile))
return None
# print 'file',self.toolfile
# clear the current liststore, search the tool file, and add each tool
tool_model = []
wear_model = []
logfile = open(self.toolfile, "r").readlines()
self.toolinfo = None
toolinfo_flag = False
for rawline in logfile:
# ignore blank lines
if rawline == '':
continue
# print 'raw:',rawline
# strip the comments from line and add directly to array
# if index = -1 the delimiter ; is missing - clear comments
index = rawline.find(";")
comment = ''
if not index == -1:
comment = (rawline[index + 1:])
comment = comment.rstrip("\n")
line = rawline.rstrip(comment)
else:
line = rawline
array = [0, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, comment]
wear_flag = False
# search beginning of each word for keyword letters
# if i = ';' that is the comment and we have already added it
# offset 0 and 1 are integers the rest floats
# we strip leading and following spaces from the comments
for offset, i in enumerate(KEYWORDS):
if i == ';': continue
for word in line.split():
if word.startswith(';'): break
if word.startswith(i):
if offset == 0:
if int(word.lstrip(i)) == self.current_tool_num:
toolinfo_flag = True
# This array's tool num is the current tool num
# remember it for later
temp = array
# check if tool is greater then 10000 -then it's a wear tool
if int(word.lstrip(i)) > 10000:
wear_flag = True
if offset in (0, 1, 14):
try:
array[offset] = int(word.lstrip(i))
except ValueError as e:
try:
array[offset] = int(float(word.lstrip(i)))
except Exception as e:
LOG.error("toolfile integer access: {} : {}".format(word.lstrip(i), e))
else:
try:
# we will call this range zero:
if float(word.lstrip(i)) < 0.000001 and float(word.lstrip(i)) > -0.000001:
array[offset] = 0.0
else:
array[offset] = float(word.lstrip(i))
except:
LOG.error("toolfile float access: {}".format(self.toolfile))
break
# add array line to model array
if wear_flag:
wear_model.append(array)
else:
tool_model.append(array)
if toolinfo_flag:
self.toolinfo = temp
else:
self.toolinfo = [0, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, 'No Tool']
# print 'load'
# for i in tool_model:
# print i
# print wear_model
return (tool_model, wear_model)
# converts from linuxcnc toolfile array to toolwear array
# linuxcnc handles toolwear by having tool wear as extra tools with tool numbers above 10000 (fanuc style)
# qtvcp just adds the extra tool wear positions (x and z) to the original array
def CONVERT_TO_WEAR_TYPE(self, data):
if data is None:
data = ([], [])
if not INFO.MACHINE_IS_LATHE:
maintool = data[0] + data[1]
weartool = []
else:
maintool = data[0]
weartool = data[1]
# print 'main',data
tool_num_list = {}
full_tool_list = []
for rnum, row in enumerate(maintool):
new_line = [False, 0, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0,
'No Tool']
valuesInRow = [value for value in row]
for cnum, i in enumerate(valuesInRow):
if cnum == 0:
# keep a dict of actual tools numbers vrs row index
tool_num_list[i] = rnum
if cnum in (0, 1, 2):
# transfer these positions directly to new line (offset by 1 for checkbox)
new_line[cnum + 1] = i
elif cnum == 3:
# move Y past x wear position
new_line[5] = i
elif cnum == 4:
# move z past y wear position
new_line[7] = i
elif cnum in (5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15):
# a;; the rest past z wear position
new_line[cnum + 4] = i
full_tool_list.append(new_line)
# print 'row',row
# print 'new row',new_line
# any tool number over 10000 is a wear offset
# It's already been separated in the weartool variable.
# now we pull the values we need out and put it in our
# full tool list's tool variable's parent tool row
# eg 10001 goes to tool 1, 10002 goes to tool 2 etc
# for now only if in lathe mode
if INFO.MACHINE_IS_LATHE:
for rnum, row in enumerate(weartool):
values = [value for value in row]
try:
parent_tool = tool_num_list[(values[0] - 10000)]
except KeyError:
LOG.error("tool wear number has no parent Tool: {}".format(values))
continue
else:
full_tool_list[parent_tool][4] = values[2]
full_tool_list[parent_tool][6] = values[3]
full_tool_list[parent_tool][8] = values[4]
return full_tool_list
# converts from toolwear array to linuxcnc toolfile array
# linuxcnc handles toolwear by having tool wear as extra tools with tool numbers above 10000 (fanuc style)
# qtvcp just adds the extra tool wear positions (x and z) to the original array
def CONVERT_TO_STANDARD_TYPE(self, data):
# for i in data:
# print i
if data is None:
data = ([])
tool_wear_list = []
full_tool_list = []
for rnum, row in enumerate(data):
new_line = [0, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, '']
new_wear_line = [0, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, 'Wear Offset']
wear_flag = False
values = [value for value in row]
for cnum, i in enumerate(values):
# print cnum, i, type(i)
if cnum in (1, 2):
new_line[cnum - 1] = int(i)
elif cnum == 3:
new_line[cnum - 1] = float(i)
elif cnum == 4 and i != '0':
wear_flag = True
new_wear_line[2] = float(i)
elif cnum == 5 and i != '0':
new_line[cnum - 2] = float(i)
elif cnum == 6 and i != '0':
wear_flag = True
new_wear_line[3] = float(i)
elif cnum == 7 and i != '0':
new_line[cnum - 3] = float(i)
elif cnum == 8 and i != '0':
wear_flag = True
new_wear_line[4] = float(i)
elif cnum in (9, 10, 11, 12, 13, 14, 15, 16, 17):
new_line[cnum - 4] = float(i)
elif cnum == 18:
new_line[cnum - 4] = int(i)
elif cnum == 19:
new_line[cnum - 4] = str(i)
if wear_flag:
new_wear_line[0] = int(values[1] + 10000)
new_wear_line[15] = 'Wear Offset %d' % values[1]
tool_wear_list.append(new_wear_line)
# add tool line to tool list
full_tool_list.append(new_line)
LOG.debug("converted line: {}".format(new_line))
# add wear list to full tool list if in lathe mode
if INFO.MACHINE_IS_LATHE:
full_tool_list = full_tool_list + tool_wear_list
return full_tool_list
# TODO check for linnuxcnc ON and IDLE which is the only safe time to edit/SAVE the tool file.
def _save(self, new_model, delete=()):
if self.toolfile == None:
return True
file = open(self.toolfile, "w")
for row in new_model:
values = [value for value in row]
line = ""
skip = False
for num, i in enumerate(values):
# print KEYWORDS[num], i, #type(i), int(i)
if num == 0 and i in delete:
LOG.debug("delete tool ' {}".format(i))
skip = True
if num in (0, 1, 14): # tool# pocket# orientation
line = line + "%s%d " % (KEYWORDS[num], i)
elif num == 15: # comments
test = i.strip()
line = line + "%s%s " % (KEYWORDS[num], test)
else:
test = float(str(i).lstrip()) # floats
if test == 0.0:
line = line + "%s%d " % (KEYWORDS[num], test)
elif num in (12, 13):
line = line + "%s%3.1f " % (KEYWORDS[num], test)
else:
line = line + "%s%.5f " % (KEYWORDS[num], test)
LOG.debug("Save line: {}".format(line))
if not skip:
print(line, file=file)
# These lines are required to make sure the OS doesn't cache the data
# That would make linuxcnc and the widget to be out of synch leading to odd errors
file.flush()
os.fsync(file.fileno())
# tell linuxcnc we changed the tool table entries
try:
ACTION.RELOAD_TOOLTABLE()
except:
LOG.error("reloading of tool table into linuxcnc: {}".format(self.toolfile))
return True
# create a hash code
def md5sum(self, filename):
try:
f = open(filename, "rb")
except:
return None
else:
return hashlib.md5(f.read()).hexdigest()
# push the update to whoever using STATUS
def emit_update(self):
data = self.GET_TOOL_MODELS()
if data is not None:
STATUS.emit('toolfile-stale', data)
# check the hash code on the toolfile against
# the saved hash code when last reloaded.
def periodic_check(self, w):
if self._delay < 9:
self._delay += 1
return
if STATUS.is_status_valid() == False:
return
self._delay = 0
m1 = self.md5sum(self.toolfile)
if m1 and self._hash_code != m1:
self._hash_code = m1
self.emit_update()
|