Import LinuxCNC source tree
This commit is contained in:
371
lib/python/qtvcp/qt_tstat.py
Normal file
371
lib/python/qtvcp/qt_tstat.py
Normal file
@@ -0,0 +1,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()
|
||||
Reference in New Issue
Block a user