Files
linuxcncAllWasm/lib/python/qtvcp/qt_tstat.py
2026-08-20 06:17:39 +08:00

372 lines
14 KiB
Python

#!/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()