preamble.py (8450B)
1 import pandas as pd 2 import math 3 4 5 import os 6 import sys 7 8 def resource_path(relative_path): 9 """ Get absolute path to resource, works for dev and for PyInstaller """ 10 try: 11 12 base_path = sys._MEIPASS 13 except Exception: 14 15 base_path = os.path.abspath(".") 16 17 return os.path.join(base_path, relative_path) 18 19 class ThreadDatabase: 20 def __init__(self, csv_path): 21 full_path = resource_path(csv_path) 22 self.df = pd.read_csv(full_path) 23 24 def lookup_value_T_d(self, diameter, pitch, grade): 25 26 row = self.df[( self.df["dmin"] < diameter) & ( diameter <= self.df["dmax"] ) & (self.df["p"] == pitch)] 27 28 if row.empty: 29 return None 30 31 value = 0.001*row.iat[0, grade] 32 33 if pd.isna(value): 34 return None 35 36 return float(value) 37 38 def lookup_value_es(self, pitch, class_letter): 39 40 value1=-0.001*self.df.loc[pitch, class_letter] 41 42 if pd.isna(value1): 43 return None 44 45 return float(value1) 46 47 48 class InputParser: 49 def __init__(self): 50 self.defaults = pd.read_csv(resource_path("defaults.csv")) 51 52 def validated(self, value): 53 try: 54 value_float = float(value) 55 return value_float 56 except ValueError: 57 return None 58 59 def parse_designation(self, designation): 60 """Return (diameter and pitch) with defaults if needed.""" 61 62 designation = designation.lower().replace("x", "×") 63 64 # Minimal format (e.g. M10) 65 if ("×" not in designation) and ("-" not in designation): 66 d = self.validated(designation.replace("m", "")) 67 # check format 68 if d is None: 69 return "Invalid diameter format!" 70 71 #check availability 72 elif not self.defaults["d"].eq(d).any(): 73 return "Diameter not available!" 74 75 else: 76 row=self.defaults[self.defaults["d"]==d] 77 pitch = row.iat[0, 1] 78 79 grade = 6 # default tolerance grade 80 if d <= 1.4: 81 class_letter = "h" # default class up to and including 1.4 nominal diameter 82 else: 83 class_letter = "g" # default class for larger diameters 84 return d, pitch, grade, class_letter 85 86 87 # Semi-full format 1 (e.g. M10×1.5) 88 89 elif ("×" in designation) and ("-" not in designation): 90 d = self.validated(designation.replace("m", "").split("×")[0]) 91 92 # check format 93 if d is None: 94 return "Invalid diameter format!" 95 96 # check availability 97 elif not self.defaults["d"].eq(d).any(): 98 return "Diameter not available!" 99 100 pitch = self.validated(designation.split("×")[1]) 101 102 # check format 103 if pitch is None: 104 return "Invalid pitch format!" 105 106 # check availability 107 elif not self.defaults["p"].eq(pitch).any(): 108 return "Pitch not available!" 109 110 # check compatibility 111 elif pitch > self.defaults[self.defaults["d"]==d].iat[0, 1]: 112 return "Incompatible pitch for given diameter!" 113 114 grade = 6 # default tolerance grade 115 if d <= 1.4: 116 class_letter = "h" # default class up to and including 1.4 nominal diameter 117 else: 118 class_letter = "g" # default class for larger diameters 119 return d, pitch, grade, class_letter 120 121 # Semi-full format 2 (e.g. M10-6g) 122 elif ("×" not in designation) and ("-" in designation): 123 try: 124 before_dash, after_dash = designation.split("-") 125 d = self.validated(before_dash.replace("m", "")) 126 if d is None: 127 return "Invalid diameter format!" 128 elif not self.defaults["d"].eq(d).any(): 129 return "Diameter not available!" 130 else: 131 row=self.defaults[self.defaults["d"]==d] 132 pitch = row.iat[0, 1] 133 try: 134 grade = int(''.join(filter(str.isdigit, after_dash))) 135 if (grade < 3) or (grade > 9): 136 return "Invalid tolerance grade!" 137 except ValueError: 138 return "Invalid tolerance grade!" 139 140 try: 141 class_letter = ''.join(filter(str.isalpha, after_dash)) 142 if len(class_letter) != 1: 143 return "Invalid class letter!" 144 except ValueError: 145 return "Invalid class letter!" 146 return d, pitch, grade, class_letter 147 except ValueError: 148 print("Error") 149 return None 150 151 # Full format (e.g. M10×1.5-6g) 152 else: 153 try: 154 before_dash, after_dash = designation.split("-") 155 d = self.validated(before_dash.replace("m", "").split("×")[0]) 156 if d is None: 157 return "Invalid diameter format!" 158 elif not self.defaults["d"].eq(d).any(): 159 return "Diameter not available!" 160 pitch = self.validated(before_dash.split("×")[1]) 161 if pitch is None: 162 return "Invalid pitch format!" 163 elif not self.defaults["p"].eq(pitch).any(): 164 return "Pitch not available!" 165 elif pitch > self.defaults[self.defaults["d"]==d].iat[0, 1]: 166 return "Incompatible pitch for given diameter!" 167 grade = int(''.join(filter(str.isdigit, after_dash))) 168 if (grade < 3) or (grade > 9): 169 return "Invalid tolerance grade!" 170 class_letter = ''.join(filter(str.isalpha, after_dash)) 171 if len(class_letter) > 1: 172 return "Invalid class letter!" 173 return d, pitch, grade, class_letter 174 except Exception: 175 print("ERROR: invalid thread format.") 176 return None 177 178 179 180 def parse_sl(self, sl): # sl = "start,length" 181 """Return (start and thread length).""" 182 183 sl = sl.replace(" ", "") 184 185 start = self.validated(sl.split(",")[0]) 186 if start is None: 187 return "Invalid start point" 188 189 length = self.validated(sl.split(",")[1]) 190 if length is None: 191 return "Invalid length" 192 return start, length 193 194 195 196 197 198 199 200 201 class ThreadCalculator: 202 203 # Functions for external thread 204 @staticmethod 205 def calc_depth(pitch, es, T_d): 206 return 0.6134 * pitch + 0.5*es + 0.25*T_d 207 208 209 ranges_fal = [ 210 ((0.5, 0.5), 0.06), 211 ((0.75, 0.75), 0.07), 212 ((0.8, 4), 0.08), 213 ((4.5, 6), 0.1) 214 ] 215 216 @staticmethod 217 def cal_fal(pitch): 218 for (pmin, pmax), value in ThreadCalculator.ranges_fal: 219 if pmin <= pitch <= pmax: 220 return value 221 return None 222 223 224 ranges_noc = [ 225 ((0.5, 0.8), 4), 226 ((1, 1), 5), 227 ((1.25, 1.5), 6), 228 ((1.75, 2), 8), 229 ((2.5, 2.5), 10), 230 ((3, 3.5), 12), 231 ((4, 5), 14), 232 ((5.5, 6), 16) 233 ] 234 235 @staticmethod 236 def calc_noc(pitch): 237 for (pmin, pmax), value in ThreadCalculator.ranges_noc: 238 if pmin <= pitch <= pmax: 239 return value 240 return None 241 242 243 # Functions for internal thread 244 245 @staticmethod 246 def half_ceil(x): 247 return math.ceil(2*x)/2 248 249 @staticmethod 250 def depth_per_storke(depth): 251 if (depth > 0) and (depth <= 6): 252 return depth 253 elif depth > 6: 254 strokes = math.ceil(depth/6) 255 return math.ceil(depth/strokes) 256 else: 257 return None