def main(svc_input, configs): logger = Logger("查询日志", verbose=True) log_file_name = "log%s_%s.txt" % (svc_input.replace("?", "#"), DateTimeUtil.get_current_datetime(is_date=True)) log_file_path = WindowsUtil.convert_win_path(os.path.join(temp_dir, log_file_name)) logger.info("[开始查询] %s" % svc_input) try: # 找到本地匹配的保修历史记录 history_zip = ZipFileSVC(zip_file_path=history_zipfile, mode='a') start_time = DateTimeUtil.get_current_datetime() # 创建出所有可能查询码 svc_generator = SVCGenerator(svc_input, logger) logger.info("创建出所有可能查询码:%s" % len(svc_generator.target_svc_set)) # 根据本地匹配的非法查询码历史,筛选出目标查询码,以及非法查询码 existed_svc = history_zip.find_file_regex(svc_generator.regex) svc_generator.generate_target_svc_batch(existed_svc, invalid_history_file_path) # 调用戴尔查询API,并将API数据转化为实体类数据 output_dell_asset_list = list([]) if svc_generator.target_svc_set: batch = Batch(logger, configs) api_dell_asset_list = batch.begin(svc_generator.target_svc_set) output_dell_asset_list = api_dell_asset_list logger.info("从API中总共得到%s个结果" % (len(api_dell_asset_list))) logger.info("将实体类序列化到本地临时TXT文件") temp_text_files_path = DellAsset.serialize_txt_batch(api_dell_asset_list, temp_dir) logger.info("将序列化临时文件存到本地zip历史记录,总数:%s" % len(temp_text_files_path)) history_zip.add_new_file_batch(temp_text_files_path) logger.info("删除临时 %s 个TXT文件" % len(temp_text_files_path)) for file_path in temp_text_files_path: FileUtil.delete_file(file_path) logger.info("将API得到的实体类和历史记录实体类合并") else: logger.warn("目标查询码为空,仅从从历史记录中导出结果") for svc in svc_generator.existed_svc_set: dell_asset_content = history_zip.get_member_content(file_name="%s.txt" % svc) output_dell_asset_list.append(DellAsset.deserialize_txt(dell_asset_content)) logger.info("添加历史记录,总共得到%s个结果" % (len(output_dell_asset_list))) excel_output_path = WindowsUtil.convert_win_path(os.path.join(excel_dir, "%s.xlsx" % svc_generator.get_file_name())) DellAsset.save_as_excel_batch(output_dell_asset_list, excel_output_path) if FileUtil.is_path_existed(excel_output_path): logger.info("存为Excel文档成功") end_time = DateTimeUtil.get_current_datetime() logger.info("总用时 %s " % DateTimeUtil.datetime_diff(start_time, end_time)) logger.info("[查询结束] 总共%s个结果 保存在:%s" % (len(output_dell_asset_list), excel_output_path)) else: logger.error("[保存结果失败] %s" % excel_output_path) except Exception as e: # 若程序出现错误失败,发送邮件 logger.error("[查询失败] 已发送报告 请等待解决") logger.error("%s\n%s" % (e, traceback.format_exc())) logger.save(log_file_path) email_api_key = configs["email_api_key"] email = Email(email_api_key, subject="[查询失败] %s %s" % (DateTimeUtil.get_current_datetime(is_date=True), svc_input)) email.add_attachment(log_file_path) email.send(cc_mode=logger.has_error)
def main(svc_input, configs): logger = Logger("查询日志", verbose=True) log_file_name = "log%s_%s.txt" % (svc_input.replace( "?", "#"), DateTimeUtil.get_current_datetime(is_date=True)) log_file_path = WindowsUtil.convert_win_path( os.path.join(temp_dir, log_file_name)) logger.info("[开始查询] %s" % svc_input) try: # 找到本地匹配的保修历史记录 history_zip = ZipFileSVC(zip_file_path=history_zipfile, mode='a') start_time = DateTimeUtil.get_current_datetime() # 创建出所有可能查询码 svc_generator = SVCGenerator(svc_input, logger) logger.info("创建出所有可能查询码:%s" % len(svc_generator.target_svc_set)) # 根据本地匹配的非法查询码历史,筛选出目标查询码,以及非法查询码 existed_svc = history_zip.find_file_regex(svc_generator.regex) svc_generator.generate_target_svc_batch(existed_svc, invalid_history_file_path) # 调用戴尔查询API,并将API数据转化为实体类数据 output_dell_asset_list = list([]) if svc_generator.target_svc_set: batch = Batch(logger, configs) api_dell_asset_list = batch.begin(svc_generator.target_svc_set) output_dell_asset_list = api_dell_asset_list logger.info("从API中总共得到%s个结果" % (len(api_dell_asset_list))) logger.info("将实体类序列化到本地临时TXT文件") temp_text_files_path = DellAsset.serialize_txt_batch( api_dell_asset_list, temp_dir) logger.info("将序列化临时文件存到本地zip历史记录,总数:%s" % len(temp_text_files_path)) history_zip.add_new_file_batch(temp_text_files_path) logger.info("删除临时 %s 个TXT文件" % len(temp_text_files_path)) for file_path in temp_text_files_path: FileUtil.delete_file(file_path) logger.info("将API得到的实体类和历史记录实体类合并") else: logger.warn("目标查询码为空,仅从从历史记录中导出结果") for svc in svc_generator.existed_svc_set: dell_asset_content = history_zip.get_member_content( file_name="%s.txt" % svc) output_dell_asset_list.append( DellAsset.deserialize_txt(dell_asset_content)) logger.info("添加历史记录,总共得到%s个结果" % (len(output_dell_asset_list))) excel_output_path = WindowsUtil.convert_win_path( os.path.join(excel_dir, "%s.xlsx" % svc_generator.get_file_name())) DellAsset.save_as_excel_batch(output_dell_asset_list, excel_output_path) if FileUtil.is_path_existed(excel_output_path): logger.info("存为Excel文档成功") end_time = DateTimeUtil.get_current_datetime() logger.info("总用时 %s " % DateTimeUtil.datetime_diff(start_time, end_time)) logger.info("[查询结束] 总共%s个结果 保存在:%s" % (len(output_dell_asset_list), excel_output_path)) else: logger.error("[保存结果失败] %s" % excel_output_path) except Exception as e: # 若程序出现错误失败,发送邮件 logger.error("[查询失败] 已发送报告 请等待解决") logger.error("%s\n%s" % (e, traceback.format_exc())) logger.save(log_file_path) email_api_key = configs["email_api_key"] email = Email( email_api_key, subject="[查询失败] %s %s" % (DateTimeUtil.get_current_datetime(is_date=True), svc_input)) email.add_attachment(log_file_path) email.send(cc_mode=logger.has_error)
class LineWidgit(tkinter.Frame): ''' This is the GUI widget that represents a single line in the output data. It uses the data_store to communicate values into and out of itself. ''' def __init__(self, parent,lineno): self.logger = Logger(self, Logger.INFO) self.logger.debug("constructor") tkinter.Frame.__init__(self, parent) self.data_store = DataStore.get_instance() self.index = lineno self.name = "Hole %d" % (lineno+1) self.line_name = tkinter.Label(self, text=self.name, width=12) self.line_name.grid(row=lineno+1, column=0, sticky=tkinter.W) self.inter_ctl = tkinter.Entry(self, width=5, validate="focusout", validatecommand=self.change_interval) self.inter_ctl.bind('<Return>', self.change_interval) self.inter_ctl.bind('<Tab>', self.change_interval) self.inter_ctl.grid(row=lineno+1, column=1) self.note_ctl_txt = tkinter.StringVar() self.note_ctl = tkinter.Label(self, textvariable=self.note_ctl_txt, width=12) self.note_ctl.grid(row=lineno+1, column=2) self.freq_ctl_txt = tkinter.StringVar() self.freq_ctl = tkinter.Label(self, textvariable=self.freq_ctl_txt, width=12) self.freq_ctl.grid(row=lineno+1, column=3) self.hole_ctl = HoleSizeWidgit(self, lineno) self.hole_ctl.config(padx=25) self.hole_ctl.grid(row=lineno+1, column=4) self.locat_ctl_txt = tkinter.StringVar() self.locat_ctl = tkinter.Label(self, textvariable=self.locat_ctl_txt, width=12) self.locat_ctl.grid(row=lineno+1, column=5) self.diff_ctl_txt = tkinter.StringVar() self.diff_ctl = tkinter.Label(self, textvariable=self.diff_ctl_txt, width=12) self.diff_ctl.grid(row=lineno+1, column=6) self.cutoff_ctl_txt = tkinter.StringVar() self.cutoff_ctl = tkinter.Label(self, textvariable=self.cutoff_ctl_txt, width=12) self.cutoff_ctl.grid(row=lineno+1, column=7) self.set_state() self.logger.debug("end constructor") @debugger def set_state(self): ''' Place the data from the data_store into the GUI. ''' self.inter_ctl.delete(0, tkinter.END) self.inter_ctl.insert(0, str(self.data_store.get_hole_interval(self.index))) self.note_ctl_txt.set(str(self.data_store.get_hole_note(self.index))) # Label self.freq_ctl_txt.set("%s Hz"%(str(self.data_store.get_hole_freq(self.index)))) self.locat_ctl_txt.set("%0.4f"%self.data_store.get_hole_xloc(self.index)) # Label self.diff_ctl_txt.set("%0.4f"%self.data_store.get_hole_diff(self.index)) # Label self.cutoff_ctl_txt.set("%0.4f"%self.data_store.get_hole_cutoff(self.index)) # Label self.hole_ctl.set_state() @debugger def get_state(self): ''' Get the data out of the display and place it in the data_store. ''' self.data_store.set_hole_interval(self.index, int(self.inter_ctl.get())) self.data_store.set_hole_note(self.index, self.note_ctl_txt.get()) # str self.data_store.set_hole_freq(self.index, float(self.freq_ctl_txt.get().split()[0])) self.data_store.set_hole_location(self.index, float(self.locat_ctl_txt.get())) self.data_store.set_hole_diff(self.index, float(self.diff_ctl_txt.get())) self.data_store.set_hole_cutoff(self.index, float(self.cutoff_ctl_txt.get())) self.hole_ctl.get_state() @debugger def print_state(self): self.logger.msg(str(self.get_state())) @debugger def change_units(self): ''' When this is called, it is assumed that the datastore and the GUI need to have the vaules updated to reflect the new units. ''' if self.data_store.get_units(): self.data_store.set_hole_size(self.index, utility.in_to_mm(self.data_store.get_hole_size(self.index))) self.data_store.set_hole_location(self.index, utility.in_to_mm(self.data_store.get_hole_location(self.index))) self.data_store.set_hole_diff(self.index, utility.in_to_mm(self.data_store.get_hole_diff(self.index))) self.data_store.set_hole_cutoff(self.index, utility.in_to_mm(self.data_store.get_hole_cutoff(self.index))) else: self.data_store.set_hole_size(self.index, utility.mm_to_in(self.data_store.get_hole_size(self.index))) self.data_store.set_hole_location(self.index, utility.mm_to_in(self.data_store.get_hole_location(self.index))) self.data_store.set_hole_diff(self.index, utility.mm_to_in(self.data_store.get_hole_diff(self.index))) self.data_store.set_hole_cutoff(self.index, utility.mm_to_in(self.data_store.get_hole_cutoff(self.index))) self.set_state() self.data_store.set_change_flag() @debugger def change_interval(self, event=None): try: val = int(self.inter_ctl.get()) oldval = self.data_store.get_hole_interval(self.index) if val != oldval: if val > 0 and val < 5: self.data_store.set_hole_interval(self.index, val) self.logger.debug("change interval from %d to %d"%(oldval, val)) raise_event("UPDATE_NOTES_EVENT") self.data_store.set_change_flag() else: self.logger.error("invalid value for interval: %s"%(str(self.inter_ctl.get()))) messagebox.showerror("ERROR", "Intervals must be an integer between 1 and 4") self.inter_ctl.delete(0, tkinter.END) self.inter_ctl.insert(0, str(self.data_store.get_hole_interval(self.index))) return False else: self.logger.debug("ignore") except ValueError: self.logger.error("invalid integer for interval: %s"%(str(self.inter_ctl.get()))) messagebox.showerror("ERROR", "Cannot convert the string \"%s\" to an integer between 1 1nd 4"%(self.inter_ctl.get())) self.inter_ctl.delete(0, tkinter.END) self.inter_ctl.insert(0, str(self.data_store.get_hole_interval(self.index))) return False except IndexError: pass # always ignore return True
class DependencyScriptConfiguration(object): @staticmethod def GetConfigurationFilename(scriptFilename): path, ext = os.path.splitext(argv[0]) path, name = os.path.split(path) return '{0}.ini'.format(name) def __init__(self, argv=None, argparser=None): # Make an argparser if we were not given one already. if argparser is None: self.argparser = argparse.ArgumentParser(description='Generates an SQLite3 database containing all of the #include dependencies in your project.') else: self.argparser = argparser # Add our arguments to the argparser. self.argparser.add_argument('-C', '--print-example-config', action='store_true', default=False, dest='printExampleConfig', help='The script requires a configuration file in the current directory or the script directory in order to run. This option generates an example config file that lists all the available options. Note: the databases must have the same filename in the config file. You will also want to make sure that the default filename, :memory:, is no longer set. If it was then you won\'t have a file to work with. Read the comments in the example config for details.') self.argparser.add_argument('--debug', action='store_true', default=False, dest='debugMessages', help='Print the debug messages. Very verbose, use only in development.') self.argparser.add_argument('--verbose', action='store_true', default=False, dest='verbose', help='Print the info messages about progress and status.') self.argparser.add_argument('--silence-errors', action='store_true', default=False, dest='silenceErrors', help='Don\'t print any errors.') self.argparser.add_argument('-c', '--config-filename', dest='scriptIni', metavar='<config-filename>', help='Specifies the path to the configuration file to use.') self.argparser.add_argument('-f', '--database-filename', dest='databaseFilename', metavar='<db-filename>', help='Specifies the filename of the database. Note: specifying this option overrides the filename in the configuration file.') self.argparser.add_argument('-s', '--source-path', dest='sourcePath', metavar='<source-path>', help='Specifies the path to the root of the source code.') # Configure the rest of our class. We need to initialize unused variables if we want to use # them later in our class. if argv is not None: self.Configure(argv) else: self.ClearConfiguration() def Configure(self, argv): self.args = argv self.messagePrinter = Logger() self.scriptPath, self.scriptExtension = os.path.splitext(argv[0]) self.scriptPath, self.scriptName = os.path.split(self.scriptPath) self.argparser.parse_args(args=argv[1:], namespace=self) self.messagePrinter.isDbgEnabled = self.debugMessages self.messagePrinter.isInfoEnabled = self.verbose self.messagePrinter.isErrEnabled = not self.silenceErrors if self.scriptIni is None: iniPath = '' if self.scriptPath is not None: iniPath = self.scriptPath self.scriptIni = toPosixPath(os.path.join(iniPath, '{0}.ini'.format(self.scriptName))) self.parser = configparser.ConfigParser(allow_no_value = True) # see https://docs.python.org/2/library/configparser.html self.parser.optionxform = str # make case-sensitive as per https://docs.python.org/2/library/configparser.html self.isConfigured = True if os.path.exists(self.scriptIni): self.parser.read(self.scriptIni) elif os.path.exists(os.path.join(self.scriptPath, self.scriptIni)): self.parser.read(os.path.join(self.scriptPath, self.scriptIni)) else: msg = "No configuration file found. Searched, current directory and script directory directory for {0}.".format(self.scriptIni) self.messagePrinter.error(msg) self.isConfigured = False self.databaseFilename = ':memory:' self.sourcePath = './' return try: if self.databaseFilename is None: self.databaseFilename = self.parser.get("Output", "DatabaseFilename") except: self.databaseFilename = ':memory:' try: if self.sourcePath is None: self.sourcePath = self.parser.get("Paths","SourceRoot") # Make the read SourceRoot path relative to the INI file's path. if self.isConfigured: iniPath, iniFilename = os.path.split(self.scriptIni) self.sourcePath = toPosixPath(os.path.normpath(os.path.join(iniPath, self.sourcePath))) print('source-path: {0}'.format(self.sourcePath)) except: self.sourcePath = './' def ClearConfiguration(self): self.args = None self.parser = None self.scriptName = None self.scriptPath = None self.scriptExtension = None self.scriptIni = None
class UpperFrame(tkinter.Frame): ''' This class manages the upper frame of the display. ''' def __init__(self, master): self.logger = Logger(self, Logger.INFO) self.logger.debug("constructor") self.master = master self.data_store = DataStore.get_instance() register_event("UPDATE_UPPER_EVENT", self.set_state) @debugger def create_frame(self): # build the screen # Fill in the upper frame tkinter.Label(self.master, text="Title").grid(row=0, column=0, sticky=tkinter.E) self.titleEntry = tkinter.Entry(self.master, width=40, validate="focusout", validatecommand=self.setTitleCommand) self.titleEntry.bind('<Return>', self.setTitleCommand) self.titleEntry.bind('<Tab>', self.setTitleCommand) self.titleEntry.grid(row=0, column=1, columnspan=3, padx=9, pady=4) tkinter.Label(self.master, text="Inside Diameter").grid(row=1, column=0, sticky=tkinter.E, pady=4) self.insideDiaEntry = tkinter.Entry(self.master, validate="focusout", validatecommand=self.insideDiaCommand) self.insideDiaEntry.bind('<Return>', self.insideDiaCommand) self.insideDiaEntry.bind('<Tab>', self.insideDiaCommand) self.insideDiaEntry.grid(row=1, column=1, pady=4) tkinter.Label(self.master, text="Wall Thickness").grid(row=1, column=2, sticky=tkinter.E, pady=4) self.wallThicknessEntry = tkinter.Entry(self.master, validate="focusout", validatecommand=self.wallThicknessCommand) self.wallThicknessEntry.bind('<Return>', self.wallThicknessCommand) self.wallThicknessEntry.bind('<Tab>', self.wallThicknessCommand) self.wallThicknessEntry.grid(row=1, column=3, pady=4) tkinter.Label(self.master, text="Number of Holes").grid(row=2, column=0, sticky=tkinter.E, pady=4) self.numHolesEntry = tkinter.Entry(self.master, validate="focusout", validatecommand=self.numHolesCommand) self.numHolesEntry.bind('<Return>', self.numHolesCommand) self.numHolesEntry.bind('<Tab>', self.numHolesCommand) self.numHolesEntry.grid(row=2, column=1, pady=4) tkinter.Label(self.master, text="Select Bell Note").grid(row=2, column=2, sticky=tkinter.E, pady=4) self.bellNoteCombo = ttk.Combobox(self.master, state="readonly", values=self.data_store.bellNoteArray) self.bellNoteCombo.config(width=17) self.bellNoteCombo.grid(row=2, column=3, pady=4) self.bellNoteCombo.bind("<<ComboboxSelected>>", self.bellSelectCallback) tkinter.Label(self.master, text="Embouchure Area").grid(row=4, column=0, sticky=tkinter.E, pady=4) self.embouchureAreaEntry = tkinter.Entry(self.master) self.embouchureAreaEntry.grid(row=4, column=1, pady=4) tkinter.Label(self.master, text="Units of Measure").grid(row=3, column=0, sticky=tkinter.E, pady=4) self.measureUnitsOpt = ttk.Combobox(self.master, state="readonly", values=["inch", "mm"]) self.measureUnitsOpt.config(width=17) self.measureUnitsOpt.grid(row=3, column=1, pady=4) self.measureUnitsOpt.bind("<<ComboboxSelected>>", self.measureUnitsCallback) tkinter.Label(self.master, text="Display Format").grid(row=3, column=2, sticky=tkinter.E, pady=4) self.displayFormatOpt = ttk.Combobox(self.master, state="readonly", values=["decimal", "fraction"]) self.displayFormatOpt.current(1) self.displayFormatOpt.config(width=17) self.displayFormatOpt.grid(row=3, column=3, pady=4) self.displayFormatOpt.bind("<<ComboboxSelected>>", self.displayFormatCallback) tkinter.Label(self.master, text="Length").grid(row=4, column=2, sticky=tkinter.E, pady=4) self.lengthEntry = tkinter.Entry(self.master) self.lengthEntry.grid(row=4, column=3, pady=4) if self.measureUnitsOpt.get() == 'mm': self.displayFormatOpt.config(state="readonly") self.refreshButton = tkinter.Button( self.master, text="Refresh", width=14, command=self.refreshButtonCommand) self.refreshButton.grid(row=5, column=0, columnspan=4, pady=4) self.set_state() # write what's in the data_store to the GUI @debugger def get_state(self): ''' Return the state of the controls in the upper half into the data store. ''' if self.displayFormatOpt.current() == 0: self.data_store.set_disp_frac(False) else: self.data_store.set_disp_frac(True) if self.measureUnitsOpt.current() == 0: self.data_store.set_units(False) else: self.data_store.set_units(True) self.data_store.set_title(self.titleEntry.get()) self.data_store.set_inside_dia(float(self.insideDiaEntry.get())) self.data_store.set_wall_thickness(float(self.wallThicknessEntry.get())) self.data_store.set_number_holes(int(self.numHolesEntry.get())) self.data_store.set_bell_note_select(self.bellNoteCombo.current()) #self.data_store.set_embouchure_area(float(self.embouchureAreaEntry.get())) self.data_store.set_bell_freq( self.data_store.note_table[self.data_store.get_bell_note_select()]['frequency']) @debugger def set_state(self): ''' Take the state from the data store and put in the GUI. ''' self.titleEntry.delete(0, tkinter.END) self.titleEntry.insert(0, self.data_store.get_title()) self.bellNoteCombo.current(self.data_store.get_bell_note_select()) self.measureUnitsOpt.current(int(self.data_store.get_units())) # it's a bool in the data_store self.displayFormatOpt.current(int(self.data_store.get_disp_frac())) # it's a bool in the data_store self.insideDiaEntry.delete(0, tkinter.END) self.insideDiaEntry.insert(0, str(self.data_store.get_inside_dia())) self.wallThicknessEntry.delete(0, tkinter.END) self.wallThicknessEntry.insert(0, str(self.data_store.get_wall_thickness())) self.numHolesEntry.delete(0, tkinter.END) self.numHolesEntry.insert(0, str(self.data_store.get_number_holes())) self.embouchureAreaEntry.config(state=tkinter.NORMAL) self.embouchureAreaEntry.delete(0, tkinter.END) self.embouchureAreaEntry.insert(0, "%0.4f"%(self.data_store.get_embouchure_area())) self.embouchureAreaEntry.config(state="readonly") self.lengthEntry.config(state=tkinter.NORMAL) self.lengthEntry.delete(0, tkinter.END) self.lengthEntry.insert(0, "%0.4f"%(self.data_store.get_length())) self.lengthEntry.config(state="readonly") @debugger def insideDiaCommand(self, event=None): try: v = self.insideDiaEntry.get() n = float(v) if self.data_store.get_inside_dia() != n: self.logger.debug("change wall from %f to %f"%(self.data_store.get_inside_dia(), n)) self.data_store.set_inside_dia(n) self.insideDiaEntry.delete(0, tkinter.END) self.insideDiaEntry.insert(0, str(n)) raise_event("CALCULATE_EVENT") self.data_store.set_change_flag() else: self.logger.debug("ignore") return True except ValueError as e: self.logger.error(str(e)) messagebox.showerror("Error", "Could not convert inside diameter to a floating point number.\nRead value was \"%s\"." % (v)) self.insideDiaEntry.delete(0, tkinter.END) self.insideDiaEntry.insert(0, str(self.data_store.get_inside_dia())) return False except IndexError: self.logger.error(str(e)) self.wallThicknessEntry.delete(0, tkinter.END) self.wallThicknessEntry.insert(0, str(self.data_store.get_wall_thickness())) except Exception as e: self.logger.error(str(e)) messagebox.showerror("Unknown Error", "Unknown exception trying to convert inside diameter to a floating point number.\nRead value was \"%s\".\nException: %s" % (v, str(e))) self.wallThicknessEntry.delete(0, tkinter.END) self.wallThicknessEntry.insert(0, str(self.data_store.get_wall_thickness())) @debugger def wallThicknessCommand(self, event=None): try: v = self.wallThicknessEntry.get() n = float(v) if n != self.data_store.get_wall_thickness(): self.logger.debug("change wall from %f to %f"%(self.data_store.get_wall_thickness(), n)) self.data_store.set_wall_thickness(n) self.wallThicknessEntry.delete(0, tkinter.END) self.wallThicknessEntry.insert(0, str(n)) raise_event("CALCULATE_EVENT") self.data_store.set_change_flag() else: self.logger.debug("ignore") return True except ValueError as e: self.logger.error(str(e)) messagebox.showerror("Error", "Could not convert wall thickness to a floating point number.\nRead value was \"%s\"." % (v)) self.wallThicknessEntry.delete(0, tkinter.END) self.wallThicknessEntry.insert(0, str(self.data_store.get_wall_thickness())) return False except IndexError: self.logger.error(str(e)) self.wallThicknessEntry.delete(0, tkinter.END) self.wallThicknessEntry.insert(0, str(self.data_store.get_wall_thickness())) except Exception as e: self.logger.error(str(e)) messagebox.showerror("Unknown Error", "Unknown exception trying convert wall thickness to a floating point number.\nRead value was \"%s\".\nException %s" % (v, str(e))) self.wallThicknessEntry.delete(0, tkinter.END) self.wallThicknessEntry.insert(0, str(self.data_store.get_wall_thickness())) @debugger def numHolesCommand(self, event=None): n = 0 try: v = self.numHolesEntry.get() n = int(v) if n >= 1 and n <= 12: # only raise the event if the number of holes is different from # what is in the data_store if n != self.data_store.get_number_holes(): self.logger.debug("change number of holes from %d to %d"%(self.data_store.get_number_holes(), n)) self.data_store.set_number_holes(n) raise_event('UPDATE_LOWER_FRAME_EVENT') self.data_store.set_change_flag() else: self.logger.debug("ignore") return True else: self.logger.error("range error on number of holes: %s"%(str(n))) messagebox.showerror("Error", message="Number of holes must be an integer between 1 and 12.\nRead value was \"%s\"." % (v)) self.numHolesEntry.delete(0, tkinter.END) self.numHolesEntry.insert(0, str(self.data_store.get_number_holes())) return False except ValueError as e: self.logger.error(str(e)) messagebox.showerror("Error", message="Could not convert number of holes to an integer.\nRead value was \"%s\"." % (v)) self.numHolesEntry.delete(0, tkinter.END) self.numHolesEntry.insert(0, str(self.data_store.get_number_holes())) return False except IndexError as e: self.logger.error(str(e)) self.numHolesEntry.delete(0, tkinter.END) self.numHolesEntry.insert(0, str(self.data_store.get_number_holes())) except Exception as e: self.logger.error(str(e)) messagebox.showerror("Unknown Error", message="Unknown exception trying to convert number of holes to an integer.\nRead value was \"%s\".\nException: %s" % (v, str(e))) self.wallThicknessEntry.delete(0, tkinter.END) self.wallThicknessEntry.insert(0, str(self.data_store.get_wall_thickness())) @debugger def displayFormatCallback(self, event): if self.displayFormatOpt.current() == 0: val = False else: val = True if val != self.data_store.get_disp_frac(): self.data_store.set_disp_frac(val) raise_event("UPDATE_HOLE_EVENT") self.logger.debug("current format set to: %s"%(str(self.data_store.get_disp_frac()))) self.data_store.set_change_flag() else: self.logger.debug("ignore") @debugger def measureUnitsCallback(self, event): if self.measureUnitsOpt.current() == 0: val = False else: val = True if self.data_store.get_units() != val: if self.measureUnitsOpt.current() == 1: self.displayFormatOpt.config(state=tkinter.DISABLED) else: self.displayFormatOpt.config(state="readonly") self.data_store.set_units(val) self.change_units() self.logger.debug("current units set to: %s"%(str(self.data_store.get_units()))) self.data_store.set_change_flag() else: self.logger.debug("ignore") @debugger def bellSelectCallback(self, event): ''' Change the data_store to match the new bell selection ''' val = self.bellNoteCombo.current() if val != self.data_store.get_bell_note_select(): self.data_store.set_bell_note_select(val) self.data_store.set_bell_freq(self.data_store.note_table[val]['frequency']) self.logger.debug("current bell selection set to: %d: %f"%(self.data_store.get_bell_note_select(), self.data_store.get_bell_freq())) raise_event("UPDATE_NOTES_EVENT") self.data_store.set_change_flag() else: self.logger.debug("ignore") self.set_state() @debugger def refreshButtonCommand(self): self.refreshButton.focus_set() self.get_state() #raise_event('UPDATE_LINES_EVENT') raise_event('UPDATE_LOWER_FRAME_EVENT') self.set_state() @debugger def change_units(self): ''' When this is called, the assumption is that the GUI and the data_store have the wrong units. This function takes what ever is in the data_sore and converts if to the units that it finds there. Then it updates the GUI. ''' if self.data_store.get_units(): # true of units are mm self.data_store.set_inside_dia(utility.in_to_mm(self.data_store.get_inside_dia())) self.data_store.set_wall_thickness(utility.in_to_mm(self.data_store.get_wall_thickness())) else: self.data_store.set_inside_dia(utility.mm_to_in(self.data_store.get_inside_dia())) self.data_store.set_wall_thickness(utility.mm_to_in(self.data_store.get_wall_thickness())) self.set_state() # Cause the other frames to update raise_event("CHANGE_UNITS_EVENT") raise_event('CALCULATE_EVENT') self.data_store.set_change_flag() @debugger def setTitleCommand(self, event=None): try: title = self.titleEntry.get() old_title = self.data_store.get_title() if title != old_title: self.data_store.set_title(title) self.logger.debug("title set to: \"%s\""%(str(self.data_store.get_title()))) self.data_store.set_change_flag() else: self.logger.debug("ignore") except IndexError: pass # always ignore
class Calculator: def __init__(self): # constant data self.logger = Logger(self, Logger.INFO) self.logger.debug("enter constructor") #self.configuration = Configuration.get_instance() self.data = DataStore.get_instance() self.logger.debug("end constructor") register_event("CALCULATE_EVENT", self.do_calc) # self.isound = self.data.get_vsound_in() # self.msound = self.data.get_vsound_mm() self.max_loop = 12 self.max_delta = 0.0001 @debugger def update(self): ''' Make all calculations based on the current state. ''' pass @debugger def do_calc(self): # self.isound = self.data.get_vsound_in() # self.msound = self.data.get_vsound_mm() self.data.clear_hole_data() if self.data.get_calc_type() == 0: self.quadratic() #self.iterative() else: self.iterative() raise_event("UPDATE_LINES_EVENT") raise_event("UPDATE_UPPER_EVENT") @debugger def closedCorrection(self, index): # b = self.data.get_inside_dia() # holeSize = self.data.get_hole_size(index) # p = (holeSize / b) * (holeSize / b) p = math.pow( self.data.get_hole_size(index) / self.data.get_inside_dia(), 2) retv = (self.data.get_wall_thickness() * p) / 4.0 return retv @debugger def effectiveThickness(self, index): return self.data.get_wall_thickness() + ( self.data.get_chim_const() * self.data.get_hole_size(index)) # C_emb = distance from theoretical start of air column to center of embouchure hole; # the air column effectively extends beyond the blow hole center by this distance. # (the cork face should be about 1 to 1.5 embouchure diameters from emb. center) # C_emb := (Bore/Demb)*(Bore/Demb)*(wall+0.75*Demb); // per spreadsheet # C_emb := (Bore/Demb)*(Bore/Demb)*(Bore/2 + wall + 0.6133*Demb/2); // an alternative # C_emb := (Bore/Demb)*(Bore/Demb)*10.84*wall*Demb/(Bore + 2*wall); // kosel's empirical fit # # The area calculated must be translated back to a diameter. Then these formulas can be # applied correctly. @debugger def embouchureCorrection(self): # this "diameter" could be square, oval or round embDia = 2.0 * math.sqrt(self.data.get_embouchure_area() / math.pi) p = math.pow(self.data.get_inside_dia() / embDia, 2) # after testing these three formulae, the only one that is close if the first one. embDst = p * (self.data.get_wall_thickness() + 0.75 * embDia) #embDst = p * (self.data.get_inside_dia() / 2 + self.data.get_wall_thickness() + 0.6133 * embDia / 2) #embDst = p * 10.84 * self.data.get_wall_thickness() * embDia / (self.data.get_inside_dia() + 2 * self.data.get_wall_thickness()) return embDst @debugger def holeSpacing(self, index): holeSpacing = 0.0 if (index == 0): holeSpacing = self.data.get_end_location( ) - self.data.get_hole_location(index) else: holeSpacing = self.data.get_hole_location( index - 1) - self.data.get_hole_location(index) return holeSpacing @debugger def endCorrection(self): return self.data.get_ecorr() * self.data.get_inside_dia( ) / 2 # original flutomat @debugger #def firstHoleDistance(self, index): def firstHoleDistance(self): # // Cfg.open[1] = Cfg.height[1] / # // ((double) pow((double) (Cfg.diacent[1]/Cfg.borecent), 2.0) + # // Cfg.height[1] * (1.0F / Cfg.hs[1])); # final double bore = hole.whistle.bore; pow = ((self.data.get_hole_size(0) / self.data.get_inside_dia()) * (self.data.get_hole_size(0) / self.data.get_inside_dia())) holeSp = self.holeSpacing(0) e = self.effectiveThickness(0) q = e * (1.0 / holeSp) r = pow + q openCorrection = e / r return openCorrection @debugger def subsequentHoleDistance(self, index): # // Cfg.open[n] = Cfg.hs[n] * 0.5F * # // ( (double) sqrt((double) 1.0F + 4.0F * Cfg.height[n] / Cfg.hs[n] * # // (double) pow((double) (Cfg.borecent / Cfg.diacent[n]), 2.0)) - 1.0F); # final double bore = hole.whistle.bore; a = self.data.get_inside_dia() / self.data.get_hole_size(index) b = a * a holeSp = self.holeSpacing(index) c = 4.0 * self.effectiveThickness(index) / holeSp * b d = math.sqrt(1.0 + c) openCorrection = holeSp * 0.5 * (d - 1.0) return openCorrection @debugger def cutoffFrequency(self, index): dist = self.data.get_hole_location( index - 1) - self.data.get_hole_location(index) sqrtTerm = math.sqrt(self.effectiveThickness(index) * dist) ratio = self.data.get_vsound() / (2 * math.pi) ratio = ratio * (self.data.get_hole_size(index) / self.data.get_inside_dia()) ratio = ratio / sqrtTerm return ratio @debugger def iterative(self): # Calculate position of end hole xEnd = self.data.get_vsound() / (2 * self.data.get_bell_freq()) xEnd = xEnd - self.endCorrection() for i in range(self.data.get_number_holes()): xEnd = xEnd - self.closedCorrection(i) self.data.set_end_location(xEnd) # find first finger hole location nominalPosition = self.data.get_vsound() / (2 * self.data.get_hole_freq(0)) self.data.set_hole_location(0, 0.0) delta = 10.0 for i in range(self.max_loop): oldPosition = self.data.get_hole_location(0) self.data.set_hole_location( 0, nominalPosition - self.firstHoleDistance()) self.data.print_data() for h in range(1, self.data.get_number_holes()): loc = self.data.get_hole_location(0) loc -= self.closedCorrection(h) self.data.set_hole_location(0, loc) delta = math.fabs(self.data.get_hole_location(0) - oldPosition) if delta < self.max_delta: break # set subsequent finger hole locations for holeNum in range(1, self.data.get_number_holes()): # final Hole hole = whistle.hole[holeNum] nominalPosition = self.data.get_vsound() / ( 2 * self.data.get_hole_freq(holeNum)) self.data.set_hole_location(holeNum, 0.0) delta = 10.0 for i in range(self.max_loop): oldPosition = self.data.get_hole_location(holeNum) self.data.set_hole_location( holeNum, nominalPosition - self.subsequentHoleDistance(holeNum)) for h in range(holeNum + 1, self.data.get_number_holes()): loc = self.data.get_hole_location(holeNum) loc -= self.closedCorrection(h) self.data.set_hole_location(holeNum, loc) delta = math.fabs( self.data.get_hole_location(holeNum) - oldPosition) self.data.set_length(self.data.get_end_location() - self.embouchureCorrection()) for holeNum in range(self.data.get_number_holes()): self.data.set_hole_cutoff(holeNum, self.cutoffFrequency(holeNum)) self.data.set_hole_rcutoff( holeNum, self.data.get_hole_cutoff(holeNum) / self.data.get_hole_freq(holeNum)) self.data.set_hole_xloc( holeNum, self.data.get_end_location() - self.data.get_hole_location(holeNum)) if holeNum == 0: self.data.set_hole_diff( 0, self.data.get_end_location() - self.data.get_hole_location(holeNum)) else: self.data.set_hole_diff( 0, self.data.get_hole_location(holeNum - 1) - self.data.get_hole_location(holeNum)) @debugger def quadratic(self): # Calculate position of end hole xEnd = self.data.get_vsound() / (2 * self.data.get_bell_freq()) xEnd = xEnd - self.endCorrection() for i in range(self.data.get_number_holes()): xEnd -= self.closedCorrection(i) self.data.set_end_location(xEnd) # Calculate the position of the first tone hole length = self.data.get_vsound() / (2 * self.data.get_hole_freq(0)) for i in range(1, self.data.get_number_holes()): length = length - self.closedCorrection(i) try: h = self.data.get_hole_size(0) a = h / self.data.get_inside_dia() a = a * a b = -(xEnd + length) * a c = (xEnd * length) * a c += self.effectiveThickness(0) * (length - xEnd) v = (b * b) - (4.0 * a * c) self.data.set_hole_location(0, (-b - math.sqrt(math.fabs(v))) / ((2 * a))) # find subsequent finger hole locations for holeNum in range(1, self.data.get_number_holes()): length = self.data.get_vsound() / ( 2.0 * self.data.get_hole_freq(holeNum)) for i in range(holeNum + 1, self.data.get_number_holes()): length = length - self.closedCorrection(i) a = 2.0 ratio = self.data.get_inside_dia() / self.data.get_hole_size( holeNum) ratio = ratio * ratio * self.effectiveThickness(holeNum) prevHole = self.data.get_hole_location(holeNum - 1) b = -prevHole - (3.0 * length) b = b + ratio c = length - ratio c = c * prevHole c = c + length * length v = (b * b) - (4 * a * c) self.data.set_hole_location( holeNum, (-b - math.sqrt(math.fabs(v))) / ((2 * a))) except ValueError as e: self.logger.error("%s: a = %f, b = %f, c = %f, v = %f" % (str(e), a, b, c, v)) #self.embouchureCorrection() self.data.set_length(self.data.get_end_location() - self.embouchureCorrection()) for holeNum in range(self.data.get_number_holes()): self.data.set_hole_cutoff(holeNum, self.cutoffFrequency(holeNum)) self.data.set_hole_rcutoff( holeNum, self.data.get_hole_cutoff(holeNum) / self.data.get_hole_freq(holeNum)) self.data.set_hole_xloc( holeNum, self.data.get_end_location() - self.data.get_hole_location(holeNum)) if holeNum == 0: self.data.set_hole_diff( 0, self.data.get_end_location() - self.data.get_hole_location(holeNum)) else: self.data.set_hole_diff( holeNum, self.data.get_hole_location(holeNum - 1) - self.data.get_hole_location(holeNum))