def run_example(): # By default, the example detects and displays all available devices and # selects the first device listed. Use the dev_id_list variable to filter # detected devices by device ID (see UL documentation for device IDs). # If use_device_detection is set to False, the board_num variable needs to # match the desired board number configured with Instacal. use_device_detection = True dev_id_list = [] board_num = 0 try: if use_device_detection: config_first_detected_device(board_num, dev_id_list) daq_dev_info = DaqDeviceInfo(board_num) if not daq_dev_info.supports_analog_input: raise Exception('Error: The DAQ device does not support ' 'analog input') print('\nActive DAQ device: ', daq_dev_info.product_name, ' (', daq_dev_info.unique_id, ')\n', sep='') ai_info = daq_dev_info.get_ai_info() ai_range = ai_info.supported_ranges[0] channel = 0 # Get a value from the device if ai_info.resolution <= 16: # Use the a_in method for devices with a resolution <= 16 value = ul.a_in(board_num, channel, ai_range) # Convert the raw value to engineering units eng_units_value = ul.to_eng_units(board_num, ai_range, value) else: # Use the a_in_32 method for devices with a resolution > 16 # (optional parameter omitted) value = ul.a_in_32(board_num, channel, ai_range) # Convert the raw value to engineering units eng_units_value = ul.to_eng_units_32(board_num, ai_range, value) # Display the raw value print('Raw Value:', value) # Display the engineering value print('Engineering Value: {:.3f}'.format(eng_units_value)) except Exception as e: print('\n', e) finally: if use_device_detection: ul.release_daq_device(board_num)
def run_example(): # By default, the example detects and displays all available devices and # selects the first device listed. Use the dev_id_list variable to filter # detected devices by device ID (see UL documentation for device IDs). # If use_device_detection is set to False, the board_num variable needs to # match the desired board number configured with Instacal. use_device_detection = True dev_id_list = [] board_num = 0 try: if use_device_detection: config_first_detected_device(board_num, dev_id_list) daq_dev_info = DaqDeviceInfo(board_num) if not daq_dev_info.supports_analog_input: raise Exception('Error: The DAQ device does not support ' 'analog input') print('\nActive DAQ device: ', daq_dev_info.product_name, ' (', daq_dev_info.unique_id, ')\n', sep='') ai_info = daq_dev_info.get_ai_info() if ai_info.num_temp_chans <= 0: raise Exception('Error: The DAQ device does not support ' 'temperature input') channel = 0 # Get the value from the device (optional parameters omitted) value = ul.t_in(board_num, channel, TempScale.CELSIUS) # Display the value print('Channel', channel, 'Value (deg C):', value) except Exception as e: print('\n', e) finally: if use_device_detection: ul.release_daq_device(board_num)
class ULAI15(UIExample): def __init__(self, master=None): super(ULAI15, self).__init__(master) # By default, the example detects all available devices and selects the # first device listed. # If use_device_detection is set to False, the board_num property needs # to match the desired board number configured with Instacal. use_device_detection = True self.board_num = 0 try: if use_device_detection: self.configure_first_detected_device() self.device_info = DaqDeviceInfo(self.board_num) self.ai_info = self.device_info.get_ai_info() example_supported = (self.ai_info.is_supported and self.ai_info.supports_scan and ScanOptions.SCALEDATA in self.ai_info.supported_scan_options) if example_supported: self.create_widgets() else: self.create_unsupported_widgets() except ULError: self.create_unsupported_widgets(True) def start_scan(self): low_chan = self.get_low_channel_num() high_chan = self.get_high_channel_num() if low_chan > high_chan: messagebox.showerror( "Error", "Low Channel Number must be greater than or equal to High " "Channel Number") self.start_button["state"] = tk.NORMAL return rate = 100 points_per_channel = 10 num_channels = high_chan - low_chan + 1 total_count = points_per_channel * num_channels range_ = self.ai_info.supported_ranges[0] # Allocate a buffer for the scan memhandle = ul.scaled_win_buf_alloc(total_count) # Check if the buffer was successfully allocated if not memhandle: messagebox.showerror("Error", "Failed to allocate memory") self.start_button["state"] = tk.NORMAL return # Convert the memhandle to a ctypes array # Note: the ctypes array will only be valid until win_buf_free # is called. # A copy of the buffer can be created using scaled_win_buf_to_array # before the memory is freed. The copy can be used at any time. array = cast(memhandle, POINTER(c_double)) try: # Run the scan ul.a_in_scan(self.board_num, low_chan, high_chan, total_count, rate, range_, memhandle, ScanOptions.SCALEDATA) # Display the values self.display_values(array, total_count, low_chan, high_chan) except ULError as e: show_ul_error(e) finally: # Free the allocated memory ul.win_buf_free(memhandle) self.start_button["state"] = tk.NORMAL def display_values(self, array, total_count, low_chan, high_chan): new_data_frame = tk.Frame(self.results_group) channel_text = [] # Add the headers for chan_num in range(low_chan, high_chan + 1): channel_text.append("Channel " + str(chan_num) + "\n") chan_count = high_chan - low_chan + 1 # Add (up to) the first 10 values for each channel to the text chan_num = low_chan for data_index in range(0, min(chan_count * 10, total_count)): channel_text[chan_num - low_chan] += ( '{:.3f}'.format(array[data_index]) + "\n") chan_num = low_chan if chan_num == high_chan else chan_num + 1 # Add the labels for each channel for chan_num in range(low_chan, high_chan + 1): chan_label = tk.Label(new_data_frame, justify=tk.LEFT, padx=3) chan_label["text"] = channel_text[chan_num - low_chan] chan_label.grid(row=0, column=chan_num - low_chan) self.data_frame.destroy() self.data_frame = new_data_frame self.data_frame.grid() def start(self): self.start_button["state"] = tk.DISABLED self.start_scan() def get_low_channel_num(self): if self.ai_info.num_chans == 1: return 0 try: return int(self.low_channel_entry.get()) except ValueError: return 0 def get_high_channel_num(self): if self.ai_info.num_chans == 1: return 0 try: return int(self.high_channel_entry.get()) except ValueError: return 0 def validate_channel_entry(self, p): if p == '': return True try: value = int(p) if value < 0 or value > self.ai_info.num_chans - 1: return False except ValueError: return False return True def create_widgets(self): '''Create the tkinter UI''' self.device_label = tk.Label(self) self.device_label.pack(fill=tk.NONE, anchor=tk.NW) self.device_label["text"] = ('Board Number ' + str(self.board_num) + ": " + self.device_info.product_name + " (" + self.device_info.unique_id + ")") main_frame = tk.Frame(self) main_frame.pack(fill=tk.X, anchor=tk.NW) curr_row = 0 if self.ai_info.num_chans > 1: channel_vcmd = self.register(self.validate_channel_entry) low_channel_entry_label = tk.Label(main_frame) low_channel_entry_label["text"] = "Low Channel Number:" low_channel_entry_label.grid( row=curr_row, column=0, sticky=tk.W) self.low_channel_entry = tk.Spinbox( main_frame, from_=0, to=max(self.ai_info.num_chans - 1, 0), validate='key', validatecommand=(channel_vcmd, '%P')) self.low_channel_entry.grid( row=curr_row, column=1, sticky=tk.W) curr_row += 1 high_channel_entry_label = tk.Label(main_frame) high_channel_entry_label["text"] = "High Channel Number:" high_channel_entry_label.grid( row=curr_row, column=0, sticky=tk.W) self.high_channel_entry = tk.Spinbox( main_frame, from_=0, validate='key', to=max(self.ai_info.num_chans - 1, 0), validatecommand=(channel_vcmd, '%P')) self.high_channel_entry.grid( row=curr_row, column=1, sticky=tk.W) initial_value = min(self.ai_info.num_chans - 1, 3) self.high_channel_entry.delete(0, tk.END) self.high_channel_entry.insert(0, str(initial_value)) curr_row += 1 self.results_group = tk.LabelFrame( self, text="Results", padx=3, pady=3) self.results_group.pack(fill=tk.X, anchor=tk.NW, padx=3, pady=3) self.data_frame = tk.Frame(self.results_group) self.data_frame.grid() button_frame = tk.Frame(self) button_frame.pack(fill=tk.X, side=tk.RIGHT, anchor=tk.SE) self.start_button = tk.Button(button_frame) self.start_button["text"] = "Start" self.start_button["command"] = self.start self.start_button.grid(row=0, column=0, padx=3, pady=3) quit_button = tk.Button(button_frame) quit_button["text"] = "Quit" quit_button["command"] = self.master.destroy quit_button.grid(row=0, column=1, padx=3, pady=3)
class ULAI03(UIExample): def __init__(self, master=None): super(ULAI03, self).__init__(master) # By default, the example detects all available devices and selects the # first device listed. # If use_device_detection is set to False, the board_num property needs # to match the desired board number configured with Instacal. use_device_detection = True self.board_num = 0 try: if use_device_detection: self.configure_first_detected_device() self.device_info = DaqDeviceInfo(self.board_num) self.ai_info = self.device_info.get_ai_info() if self.ai_info.is_supported and self.ai_info.supports_scan: self.create_widgets() else: self.create_unsupported_widgets() except ULError: self.create_unsupported_widgets() def start_scan(self): self.low_chan = self.get_low_channel_num() self.high_chan = self.get_high_channel_num() self.num_chans = self.high_chan - self.low_chan + 1 if self.low_chan > self.high_chan: messagebox.showerror( "Error", "Low Channel Number must be greater than or equal to High " "Channel Number") self.set_ui_idle_state() return rate = 100 points_per_channel = 1000 total_count = points_per_channel * self.num_chans ai_range = self.ai_info.supported_ranges[0] # Allocate a buffer for the scan if self.ai_info.resolution <= 16: # Use the win_buf_alloc method for devices with a resolution <= # 16 self.memhandle = ul.win_buf_alloc(total_count) # Convert the memhandle to a ctypes array # Use the memhandle_as_ctypes_array method for devices with a # resolution <= 16 self.ctypes_array = cast(self.memhandle, POINTER(c_ushort)) else: # Use the win_buf_alloc_32 method for devices with a resolution # > 16 self.memhandle = ul.win_buf_alloc_32(total_count) # Use the memhandle_as_ctypes_array_32 method for devices with a # resolution > 16 self.ctypes_array = cast(self.memhandle, POINTER(c_ulong)) # Note: the ctypes array will no longer be valid after # win_buf_free is called. # A copy of the buffer can be created using win_buf_to_array # or win_buf_to_array_32 before the memory is freed. The copy # can be used at any time. # Check if the buffer was successfully allocated if not self.memhandle: messagebox.showerror("Error", "Failed to allocate memory") self.set_ui_idle_state() return # Create the frames that will hold the data self.recreate_data_frame() try: # Start the scan ul.a_in_scan(self.board_num, self.low_chan, self.high_chan, total_count, rate, ai_range, self.memhandle, ScanOptions.BACKGROUND) except ULError as e: show_ul_error(e) self.set_ui_idle_state() return # Start updating the displayed values self.update_displayed_values() def update_displayed_values(self): # Get the status from the device status, curr_count, curr_index = ul.get_status(self.board_num, FunctionType.AIFUNCTION) # Display the status info self.update_status_labels(status, curr_count, curr_index) # Display the values self.display_values(curr_index, curr_count) # Call this method again until the stop button is pressed if status == Status.RUNNING: self.after(100, self.update_displayed_values) else: # Free the allocated memory ul.win_buf_free(self.memhandle) # Stop the background operation (this is required even if the # scan completes successfully) ul.stop_background(self.board_num, FunctionType.AIFUNCTION) self.set_ui_idle_state() def update_status_labels(self, status, curr_count, curr_index): if status == Status.IDLE: self.status_label["text"] = "Idle" else: self.status_label["text"] = "Running" self.index_label["text"] = str(curr_index) self.count_label["text"] = str(curr_count) def display_values(self, curr_index, curr_count): per_channel_display_count = 10 array = self.ctypes_array low_chan = self.low_chan high_chan = self.high_chan channel_text = [] # Add the headers for chan_num in range(low_chan, high_chan + 1): channel_text.append("Channel " + str(chan_num) + "\n") # If no data has been gathered, don't add data to the labels if curr_count > 1: chan_count = high_chan - low_chan + 1 chan_num = low_chan # curr_index points to the start of the last completed channel # scan that was transferred between the board and the data # buffer. Based on this, calculate the first index we want to # display using subtraction. first_index = max(curr_index - ((per_channel_display_count - 1) * chan_count), 0) last_index = first_index + min(chan_count * per_channel_display_count, curr_count) # Add (up to) the latest 10 values for each channel to the text for data_index in range(first_index, last_index): channel_text[chan_num - low_chan] += (str(array[data_index]) + "\n") chan_num = low_chan if chan_num == high_chan else chan_num + 1 # Update the labels for each channel for chan_num in range(low_chan, high_chan + 1): chan_index = chan_num - low_chan self.chan_labels[chan_index]["text"] = channel_text[chan_index] def recreate_data_frame(self): low_chan = self.low_chan high_chan = self.high_chan new_data_frame = tk.Frame(self.inner_data_frame) self.chan_labels = [] # Add the labels for each channel for chan_num in range(low_chan, high_chan + 1): chan_label = tk.Label(new_data_frame, justify=tk.LEFT, padx=3) chan_label.grid(row=0, column=chan_num - low_chan) self.chan_labels.append(chan_label) self.data_frame.destroy() self.data_frame = new_data_frame self.data_frame.grid() def stop(self): ul.stop_background(self.board_num, FunctionType.AIFUNCTION) def set_ui_idle_state(self): self.high_channel_entry["state"] = tk.NORMAL self.low_channel_entry["state"] = tk.NORMAL self.start_button["command"] = self.start self.start_button["text"] = "Start" def start(self): self.high_channel_entry["state"] = tk.DISABLED self.low_channel_entry["state"] = tk.DISABLED self.start_button["command"] = self.stop self.start_button["text"] = "Stop" self.start_scan() def get_low_channel_num(self): if self.ai_info.num_chans == 1: return 0 try: return int(self.low_channel_entry.get()) except ValueError: return 0 def get_high_channel_num(self): if self.ai_info.num_chans == 1: return 0 try: return int(self.high_channel_entry.get()) except ValueError: return 0 def validate_channel_entry(self, p): if p == '': return True try: value = int(p) if value < 0 or value > self.ai_info.num_chans - 1: return False except ValueError: return False return True def create_widgets(self): '''Create the tkinter UI''' self.device_label = tk.Label(self) self.device_label.pack(fill=tk.NONE, anchor=tk.NW) self.device_label["text"] = ('Board Number ' + str(self.board_num) + ": " + self.device_info.product_name + " (" + self.device_info.unique_id + ")") main_frame = tk.Frame(self) main_frame.pack(fill=tk.X, anchor=tk.NW) curr_row = 0 if self.ai_info.num_chans > 1: channel_vcmd = self.register(self.validate_channel_entry) low_channel_entry_label = tk.Label(main_frame) low_channel_entry_label["text"] = "Low Channel Number:" low_channel_entry_label.grid( row=curr_row, column=0, sticky=tk.W) self.low_channel_entry = tk.Spinbox( main_frame, from_=0, to=max(self.ai_info.num_chans - 1, 0), validate='key', validatecommand=(channel_vcmd, '%P')) self.low_channel_entry.grid( row=curr_row, column=1, sticky=tk.W) curr_row += 1 high_channel_entry_label = tk.Label(main_frame) high_channel_entry_label["text"] = "High Channel Number:" high_channel_entry_label.grid( row=curr_row, column=0, sticky=tk.W) self.high_channel_entry = tk.Spinbox( main_frame, from_=0, validate='key', to=max(self.ai_info.num_chans - 1, 0), validatecommand=(channel_vcmd, '%P')) self.high_channel_entry.grid( row=curr_row, column=1, sticky=tk.W) initial_value = min(self.ai_info.num_chans - 1, 3) self.high_channel_entry.delete(0, tk.END) self.high_channel_entry.insert(0, str(initial_value)) curr_row += 1 self.results_group = tk.LabelFrame( self, text="Results", padx=3, pady=3) self.results_group.pack(fill=tk.X, anchor=tk.NW, padx=3, pady=3) self.results_group.grid_columnconfigure(1, weight=1) curr_row = 0 status_left_label = tk.Label(self.results_group) status_left_label["text"] = "Status:" status_left_label.grid(row=curr_row, column=0, sticky=tk.W) self.status_label = tk.Label(self.results_group) self.status_label["text"] = "Idle" self.status_label.grid(row=curr_row, column=1, sticky=tk.W) curr_row += 1 index_left_label = tk.Label(self.results_group) index_left_label["text"] = "Index:" index_left_label.grid(row=curr_row, column=0, sticky=tk.W) self.index_label = tk.Label(self.results_group) self.index_label["text"] = "-1" self.index_label.grid(row=curr_row, column=1, sticky=tk.W) curr_row += 1 count_left_label = tk.Label(self.results_group) count_left_label["text"] = "Count:" count_left_label.grid(row=curr_row, column=0, sticky=tk.W) self.count_label = tk.Label(self.results_group) self.count_label["text"] = "0" self.count_label.grid(row=curr_row, column=1, sticky=tk.W) curr_row += 1 self.inner_data_frame = tk.Frame(self.results_group) self.inner_data_frame.grid( row=curr_row, column=0, columnspan=2, sticky=tk.W) self.data_frame = tk.Frame(self.inner_data_frame) self.data_frame.grid() button_frame = tk.Frame(self) button_frame.pack(fill=tk.X, side=tk.RIGHT, anchor=tk.SE) self.start_button = tk.Button(button_frame) self.start_button["text"] = "Start" self.start_button["command"] = self.start self.start_button.grid(row=0, column=0, padx=3, pady=3) self.quit_button = tk.Button(button_frame) self.quit_button["text"] = "Quit" self.quit_button["command"] = self.master.destroy self.quit_button.grid(row=0, column=1, padx=3, pady=3)
class ULTI02(UIExample): def __init__(self, master): super(ULTI02, self).__init__(master) # By default, the example detects all available devices and selects the # first device listed. # If use_device_detection is set to False, the board_num property needs # to match the desired board number configured with Instacal. # BOARD NUMBER IS DEFINED IN TKINTER AT START OF TEST use_device_detection = False self.running = False # Device Detection not used in this test try: if use_device_detection: self.configure_first_detected_device() self.device_info = DaqDeviceInfo(self.board_num) self.ai_info = self.device_info.get_ai_info() if self.ai_info.temp_supported: self.create_widgets() else: self.create_unsupported_widgets() except ULError: self.create_unsupported_widgets(True) # Main update loop that gets repeated every 100ms while the test is running def update_values(self): try: # Get the values from the device (optional parameters omitted) # Temp scale set to NOSCALE and conversion will be done via lookup table within this program err_code, data_array = ul.t_in_scan(self.board_num, 0, 7, TempScale.NOSCALE) # create array of temperature and resistances to be logged, data_array is raw resistance data_example = "" data_example_temps = "" data_temp_display = numpy.array(data_array) data_array = numpy.around(data_array, 3) temp_array = interp_resist_to_temp_np1000(data_array) for x in range(0, 8): data_example += str(data_array[x]) + ';' data_example_temps += str( numpy.around(interp_resist_to_temp_np1000(data_array[x]), 2)) + ';' # Check err_code for OUTOFRANGE or OPENCONNECTION. All other # error codes will raise a ULError and are checked by the except # clause. # if err_code == ErrorCode.OUTOFRANGE: # self.warning_label["text"] = ( # "A thermocouple input is out of range.") # elif err_code == ErrorCode.OPENCONNECTION: # self.warning_label["text"] = ( # "A thermocouple input has an open connection.") # else: # self.warning_label["text"] = "" self.display_values(temp_array) # creates variables for time format in log current_date_and_time = datetime.datetime.now() current_date_and_time_string = current_date_and_time.strftime( "%Y-%m-%d %H:%M:%S") current_time = time.time() # start_logging Boolean is used to only log at 00 or 30 seconds of the minute # this is to have synched timings between two programs running independantly on the same machine sub_time = current_time - self.start_time seconds = datetime.datetime.now().strftime("%S") if seconds == '30' or seconds == '00': start_logging = True else: start_logging = False # Log at 00 or 30 seconds every minute (twice a minute) if sub_time >= 10 and start_logging: self.start_time = time.time() self.last_logged_time = datetime.datetime.now().strftime( "%Y-%m-%d %H:%M:%S") # create log of temperature for the specified time try: with open(self.filefullname, "a") as myfile: myfile.write(current_date_and_time_string + ';') myfile.write(data_example_temps + data_example + '\n') myfile.close() except: pass # Call this method again until the stop button is pressed (or an # error occurs) # This is the display in the command prompt message_timing = current_time - self.start_time_timing if message_timing <= 1 and self.verify == 1: os.system('cls') print('Test Running') print('Test Name =', self.test_name) print('Last logged data:', self.last_logged_time) for x in range(0, 8): print( round( interp_resist_to_temp_np1000(data_temp_display[x]), 2), '°C', "\t", self.channel_list[x]) self.verify = 2 elif message_timing >= 1 and message_timing <= 2 and self.verify == 2: os.system('cls') print('Test Running.') print('Test Name =', self.test_name) print('Last logged data:', self.last_logged_time) for x in range(0, 8): print( round( interp_resist_to_temp_np1000(data_temp_display[x]), 2), '°C', "\t", self.channel_list[x]) self.verify = 3 elif message_timing >= 2 and message_timing <= 3 and self.verify == 3: os.system('cls') print('Test Running..') print('Test Name =', self.test_name) print('Last logged data:', self.last_logged_time) for x in range(0, 8): print( round( interp_resist_to_temp_np1000(data_temp_display[x]), 2), '°C', "\t", self.channel_list[x]) self.verify = 4 elif message_timing >= 3 and message_timing <= 4 and self.verify == 4: os.system('cls') print('Test Running...') print('Test Name =', self.test_name) print('Last logged data:', self.last_logged_time) for x in range(0, 8): print( round( interp_resist_to_temp_np1000(data_temp_display[x]), 2), '°C', "\t", self.channel_list[x]) self.verify = 5 elif message_timing > 4 and self.verify == 5: self.start_time_timing = time.time() self.verify = 1 else: pass # self-updating graph for easy visualisation # keep running after 100 ms if self.running is True if self.running: self.after(100, self.update_values) except ULError as e: self.stop() show_ul_error(e) def display_values(self, array): low_chan = 0 high_chan = 7 for chan_num in range(low_chan, high_chan + 1): index = chan_num - low_chan self.data_labels[index]["text"] = '{:.3f}'.format( array[index]) + "\n" def stop(self): self.running = False self.start_button["command"] = self.start self.start_button["text"] = "Start" # self.low_channel_entry["state"] = tk.NORMAL # self.high_channel_entry["state"] = tk.NORMAL def start(self): os.system('cls') self.running = True self.start_button["command"] = self.stop self.start_button["text"] = "Stop" # self.low_channel_entry["state"] = tk.DISABLED # self.high_channel_entry["state"] = tk.DISABLED self.test_name_entry["state"] = tk.DISABLED self.board_number_entry["state"] = tk.DISABLED # self.low_chan = self.get_low_channel_num() # self.high_chan = self.get_high_channel_num() self.test_name = self.get_test_name() self.channel0 = self.get_channel0() self.channel1 = self.get_channel1() self.channel2 = self.get_channel2() self.channel3 = self.get_channel3() self.channel4 = self.get_channel4() self.channel5 = self.get_channel5() self.channel6 = self.get_channel6() self.channel7 = self.get_channel7() self.channel_list = [ self.channel0, self.channel1, self.channel2, self.channel3, self.channel4, self.channel5, self.channel6, self.channel7 ] self.board_num = self.get_board_num() self.recreate_data_frame() self.start_time = time.time() self.start_time_timing = time.time() self.last_logged_time = 'Nothing logged yet' self.verify = 1 self.inc = 0 self.create_log_file() self.update_values() def create_log_file(self): # creates log_file when starting program if not os.path.exists('logs'): os.makedirs('logs') current_date_and_time = datetime.datetime.now() current_date_and_time_filename = current_date_and_time.strftime( "%Y_%m_%d-%H%M%S") + '_' dir = 'logs\\' filename = current_date_and_time_filename filemission = self.test_name file_ext = '.txt' self.filefullname = dir + filename + filemission + file_ext file = open(self.filefullname, 'a') # def get_low_channel_num(self): # try: # return int(self.low_channel_entry.get()) # except ValueError: # return 0 # def get_high_channel_num(self): # try: # return int(self.high_channel_entry.get()) # except ValueError: # return 0 def get_test_name(self): try: return self.test_name_entry.get("1.0", 'end-1c') except ValueError: return 0 def get_channel0(self): try: return self.channel0_name_entry.get("1.0", 'end-1c') except ValueError: return 0 def get_channel1(self): try: return self.channel1_name_entry.get("1.0", 'end-1c') except ValueError: return 0 def get_channel2(self): try: return self.channel2_name_entry.get("1.0", 'end-1c') except ValueError: return 0 def get_channel3(self): try: return self.channel3_name_entry.get("1.0", 'end-1c') except ValueError: return 0 def get_channel4(self): try: return self.channel4_name_entry.get("1.0", 'end-1c') except ValueError: return 0 def get_channel5(self): try: return self.channel5_name_entry.get("1.0", 'end-1c') except ValueError: return 0 def get_channel6(self): try: return self.channel6_name_entry.get("1.0", 'end-1c') except ValueError: return 0 def get_channel7(self): try: return self.channel7_name_entry.get("1.0", 'end-1c') except ValueError: return 0 def get_board_num(self): try: return int(self.board_number_entry.get()) except ValueError: return 0 def validate_channel_entry(self, p): if p == '': return True try: value = int(p) if value < 0 or value > self.ai_info.num_temp_chans - 1: return False except ValueError: return False return True def recreate_data_frame(self): low_chan = 0 high_chan = 7 channels_per_row = 4 new_data_frame = tk.Frame(self.results_group) self.data_labels = [] row = 0 column = 0 # Add the labels for each channel for chan_num in range(low_chan, high_chan + 1): chan_label = tk.Label(new_data_frame, justify=tk.LEFT, padx=3) chan_label["text"] = "Channel " + str(chan_num) chan_label.grid(row=row, column=column) data_label = tk.Label(new_data_frame, justify=tk.LEFT, padx=3) data_label.grid(row=row + 1, column=column) self.data_labels.append(data_label) column += 1 if column >= channels_per_row: row += 2 column = 0 self.data_frame.destroy() self.data_frame = new_data_frame self.data_frame.pack(side=tk.TOP) def create_widgets(self): '''Create the tkinter UI''' self.device_label = tk.Label(self) self.device_label.pack(fill=tk.NONE, anchor=tk.NW) self.device_label["text"] = ('Board Number ' + str(self.board_num) + ": " + self.device_info.product_name + " (" + self.device_info.unique_id + ")") main_frame = tk.Frame(self) main_frame.pack(fill=tk.X, anchor=tk.NW) channel_vcmd = self.register(self.validate_channel_entry) curr_row = 0 if self.ai_info.num_temp_chans > 1: # # Defining low channel entry # low_channel_entry_label = tk.Label(main_frame) # low_channel_entry_label["text"] = "Low Channel Number:" # low_channel_entry_label.grid( # row=curr_row, column=0, sticky=tk.W) # self.low_channel_entry = tk.Spinbox( # main_frame, from_=0, # to=max(self.ai_info.num_temp_chans - 1, 0), # validate='key', validatecommand=(channel_vcmd, '%P')) # self.low_channel_entry.grid( # row=curr_row, column=1, sticky=tk.W) # # Defining high channel entry # curr_row += 1 # high_channel_entry_label = tk.Label(main_frame) # high_channel_entry_label["text"] = "High Channel Number:" # high_channel_entry_label.grid( # row=curr_row, column=0, sticky=tk.W) # self.high_channel_entry = tk.Spinbox( # main_frame, from_=0, # to=max(self.ai_info.num_temp_chans - 1, 0), # validate='key', validatecommand=(channel_vcmd, '%P')) # self.high_channel_entry.grid( # row=curr_row, column=1, sticky=tk.W) # Defining test name entry curr_row += 1 test_name_entry_label = tk.Label(main_frame) test_name_entry_label["text"] = "Test Name" test_name_entry_label.grid(row=curr_row, column=0, sticky=tk.W) self.test_name_entry = tk.Text(main_frame, height=1, width=30) self.test_name_entry.grid(row=curr_row, column=1, sticky=tk.W) self.test_name_entry.insert(tk.END, 'Test Name') # Defining channel #0 name curr_row += 1 channel0_name_entry_label = tk.Label(main_frame) channel0_name_entry_label["text"] = "Channel 0" channel0_name_entry_label.grid(row=curr_row, column=0, sticky=tk.W) self.channel0_name_entry = tk.Text(main_frame, height=1, width=30) self.channel0_name_entry.grid(row=curr_row, column=1, sticky=tk.W) self.channel0_name_entry.insert(tk.END, 'Channel 0') # Defining channel #1 name curr_row += 1 channel1_name_entry_label = tk.Label(main_frame) channel1_name_entry_label["text"] = "Channel 1" channel1_name_entry_label.grid(row=curr_row, column=0, sticky=tk.W) self.channel1_name_entry = tk.Text(main_frame, height=1, width=30) self.channel1_name_entry.grid(row=curr_row, column=1, sticky=tk.W) self.channel1_name_entry.insert(tk.END, 'Channel 1') # Defining channel #2 name curr_row += 1 channel2_name_entry_label = tk.Label(main_frame) channel2_name_entry_label["text"] = "Channel 2" channel2_name_entry_label.grid(row=curr_row, column=0, sticky=tk.W) self.channel2_name_entry = tk.Text(main_frame, height=1, width=30) self.channel2_name_entry.grid(row=curr_row, column=1, sticky=tk.W) self.channel2_name_entry.insert(tk.END, 'Channel 2') # Defining channel #3 name curr_row += 1 channel3_name_entry_label = tk.Label(main_frame) channel3_name_entry_label["text"] = "Channel 3" channel3_name_entry_label.grid(row=curr_row, column=0, sticky=tk.W) self.channel3_name_entry = tk.Text(main_frame, height=1, width=30) self.channel3_name_entry.grid(row=curr_row, column=1, sticky=tk.W) self.channel3_name_entry.insert(tk.END, 'Channel 3') # Defining channel #4 name curr_row += 1 channel4_name_entry_label = tk.Label(main_frame) channel4_name_entry_label["text"] = "Channel 4" channel4_name_entry_label.grid(row=curr_row, column=0, sticky=tk.W) self.channel4_name_entry = tk.Text(main_frame, height=1, width=30) self.channel4_name_entry.grid(row=curr_row, column=1, sticky=tk.W) self.channel4_name_entry.insert(tk.END, 'Channel 4') # Defining channel #5 name curr_row += 1 channel5_name_entry_label = tk.Label(main_frame) channel5_name_entry_label["text"] = "Channel 5" channel5_name_entry_label.grid(row=curr_row, column=0, sticky=tk.W) self.channel5_name_entry = tk.Text(main_frame, height=1, width=30) self.channel5_name_entry.grid(row=curr_row, column=1, sticky=tk.W) self.channel5_name_entry.insert(tk.END, 'Channel 5') # Defining channel #6 name curr_row += 1 channel6_name_entry_label = tk.Label(main_frame) channel6_name_entry_label["text"] = "Channel 6" channel6_name_entry_label.grid(row=curr_row, column=0, sticky=tk.W) self.channel6_name_entry = tk.Text(main_frame, height=1, width=30) self.channel6_name_entry.grid(row=curr_row, column=1, sticky=tk.W) self.channel6_name_entry.insert(tk.END, 'Channel 6') # Defining channel #7 name curr_row += 1 channel7_name_entry_label = tk.Label(main_frame) channel7_name_entry_label["text"] = "Channel 7" channel7_name_entry_label.grid(row=curr_row, column=0, sticky=tk.W) self.channel7_name_entry = tk.Text(main_frame, height=1, width=30) self.channel7_name_entry.grid(row=curr_row, column=1, sticky=tk.W) self.channel7_name_entry.insert(tk.END, 'Channel 7') # Defining board number entry curr_row += 1 board_number_entry_label = tk.Label(main_frame) board_number_entry_label["text"] = "Board number:" board_number_entry_label.grid(row=curr_row, column=0, sticky=tk.W) self.board_number_entry = tk.Spinbox(main_frame, from_=0, to=4, validate='key', validatecommand=(channel_vcmd, '%P')) self.board_number_entry.grid(row=curr_row, column=1, sticky=tk.W) # Default Values # initial_value = min(self.ai_info.num_temp_chans - 1, 7) # self.high_channel_entry.delete(0, tk.END) # self.high_channel_entry.insert(0, str(initial_value)) self.results_group = tk.LabelFrame(self, text="Results") self.results_group.pack(fill=tk.X, anchor=tk.NW, padx=3, pady=3) self.data_frame = tk.Frame(self.results_group) self.data_frame.pack(side=tk.TOP) self.warning_label = tk.Label(self.results_group, fg="red") self.warning_label.pack(side=tk.BOTTOM) button_frame = tk.Frame(self) button_frame.pack(fill=tk.X, side=tk.RIGHT, anchor=tk.SE) button_frame = tk.Frame(self) button_frame.pack(fill=tk.X, side=tk.RIGHT, anchor=tk.SE) self.start_button = tk.Button(button_frame) self.start_button["text"] = "Start" self.start_button["command"] = self.start self.start_button.grid(row=0, column=0, padx=3, pady=3) quit_button = tk.Button(button_frame) quit_button["text"] = "Quit" quit_button["command"] = self.master.destroy quit_button.grid(row=0, column=1, padx=3, pady=3)
class ULAIO01(UIExample): def __init__(self, master=None): super(ULAIO01, self).__init__(master) # By default, the example detects all available devices and selects the # first device listed. # If use_device_detection is set to False, the board_num property needs # to match the desired board number configured with Instacal. use_device_detection = True self.board_num = 0 try: if use_device_detection: self.configure_first_detected_device() self.device_info = DaqDeviceInfo(self.board_num) self.ai_info = self.device_info.get_ai_info() self.ao_info = self.device_info.get_ao_info() example_supported = (self.ai_info.is_supported and self.ai_info.supports_scan and self.ao_info.is_supported and self.ao_info.supports_scan) if example_supported: self.create_widgets() else: self.create_unsupported_widgets() except ULError: self.create_unsupported_widgets(True) def start_input_scan(self): self.input_low_chan = self.get_input_low_channel_num() self.input_high_chan = self.get_input_high_channel_num() self.num_input_chans = self.input_high_chan - self.input_low_chan + 1 if self.input_low_chan > self.input_high_chan: messagebox.showerror( "Error", "Low Channel Number must be greater than or equal to High " "Channel Number") self.set_input_ui_idle_state() return rate = 100 points_per_channel = 1000 total_count = points_per_channel * self.num_input_chans range_ = self.ai_info.supported_ranges[0] scan_options = ScanOptions.BACKGROUND | ScanOptions.CONTINUOUS # Allocate a buffer for the scan if self.ai_info.resolution <= 16: # Use the win_buf_alloc method for devices with a resolution <= # 16 self.input_memhandle = ul.win_buf_alloc(total_count) else: # Use the win_buf_alloc_32 method for devices with a resolution # > 16 self.input_memhandle = ul.win_buf_alloc_32(total_count) if not self.input_memhandle: messagebox.showerror("Error", "Failed to allocate memory") self.set_input_ui_idle_state() return # Create the frames that will hold the data self.recreate_input_data_frame() try: # Run the scan ul.a_in_scan(self.board_num, self.input_low_chan, self.input_high_chan, total_count, rate, range_, self.input_memhandle, scan_options) except ULError as e: show_ul_error(e) self.set_input_ui_idle_state() return # Convert the input_memhandle to a ctypes array # Note: the ctypes array will no longer be valid after win_buf_free is # called. A copy of the buffer can be created using win_buf_to_array # or win_buf_to_array_32 before the memory is freed. The copy can # be used at any time. if self.ai_info.resolution <= 16: # Use the memhandle_as_ctypes_array method for devices with a # resolution <= 16 self.ctypes_array = cast(self.input_memhandle, POINTER(c_ushort)) else: # Use the memhandle_as_ctypes_array_32 method for devices with a # resolution > 16 self.ctypes_array = cast(self.input_memhandle, POINTER(c_ulong)) # Start updating the displayed values self.update_input_displayed_values(range_) def update_input_displayed_values(self, range_): # Get the status from the device status, curr_count, curr_index = ul.get_status(self.board_num, FunctionType.AIFUNCTION) # Display the status info self.update_input_status_labels(status, curr_count, curr_index) # Display the values self.display_input_values(range_, curr_index, curr_count) # Call this method again until the stop_input button is pressed if status == Status.RUNNING: self.after(100, self.update_input_displayed_values, range_) else: # Free the allocated memory ul.win_buf_free(self.input_memhandle) self.set_input_ui_idle_state() def update_input_status_labels(self, status, curr_count, curr_index): if status == Status.IDLE: self.input_status_label["text"] = "Idle" else: self.input_status_label["text"] = "Running" self.input_index_label["text"] = str(curr_index) self.input_count_label["text"] = str(curr_count) def display_input_values(self, range_, curr_index, curr_count): per_channel_display_count = 10 array = self.ctypes_array low_chan = self.input_low_chan high_chan = self.input_high_chan channel_text = [] # Add the headers for chan_num in range(low_chan, high_chan + 1): channel_text.append("Channel " + str(chan_num) + "\n") # If no data has been gathered, don't add data to the labels if curr_count > 1: chan_count = high_chan - low_chan + 1 chan_num = low_chan # curr_index points to the start_input of the last completed # channel scan that was transferred between the board and the data # buffer. Based on this, calculate the first index we want to # display using subtraction. first_index = max(curr_index - ((per_channel_display_count - 1) * chan_count), 0) # Add (up to) the latest 10 values for each channel to the text for data_index in range( first_index, first_index + min( chan_count * per_channel_display_count, curr_count)): raw_value = array[data_index] if self.ai_info.resolution <= 16: eng_value = ul.to_eng_units(self.board_num, range_, raw_value) else: eng_value = ul.to_eng_units_32(self.board_num, range_, raw_value) channel_text[chan_num - low_chan] += ( '{:.3f}'.format(eng_value) + "\n") chan_num = low_chan if chan_num == high_chan else chan_num + 1 # Update the labels for each channel for chan_num in range(low_chan, high_chan + 1): chan_index = chan_num - low_chan self.chan_labels[chan_index]["text"] = channel_text[chan_index] def recreate_input_data_frame(self): low_chan = self.input_low_chan high_chan = self.input_high_chan new_data_frame = tk.Frame(self.input_inner_data_frame) self.chan_labels = [] # Add the labels for each channel for chan_num in range(low_chan, high_chan + 1): chan_label = tk.Label(new_data_frame, justify=tk.LEFT, padx=3) chan_label.grid(row=0, column=chan_num - low_chan) self.chan_labels.append(chan_label) self.data_frame.destroy() self.data_frame = new_data_frame self.data_frame.grid() def exit(self): self.stop_input() self.stop_output() self.master.destroy() def start_output_scan(self): # Build the data array self.output_low_chan = self.get_output_low_channel_num() self.output_high_chan = self.get_output_high_channel_num() self.num_output_chans = ( self.output_high_chan - self.output_low_chan + 1) if self.output_low_chan > self.output_high_chan: messagebox.showerror( "Error", "Low Channel Number must be greater than or equal to High " "Channel Number") self.set_output_ui_idle_state() return points_per_channel = 1000 rate = 1000 num_points = self.num_output_chans * points_per_channel scan_options = (ScanOptions.BACKGROUND | ScanOptions.CONTINUOUS | ScanOptions.SCALEDATA) ao_range = self.ao_info.supported_ranges[0] self.output_memhandle = ul.scaled_win_buf_alloc(num_points) # Check if the buffer was successfully allocated if not self.output_memhandle: messagebox.showerror("Error", "Failed to allocate memory") self.output_start_button["state"] = tk.NORMAL return try: data_array = cast(self.output_memhandle, POINTER(c_double)) frequencies = self.add_output_example_data( data_array, ao_range, self.num_output_chans, rate, points_per_channel) self.recreate_freq_frame() self.display_output_signal_info(frequencies) ul.a_out_scan( self.board_num, self.output_low_chan, self.output_high_chan, num_points, rate, ao_range, self.output_memhandle, scan_options) # Start updating the displayed values self.update_output_displayed_values() except ULError as e: show_ul_error(e) self.set_output_ui_idle_state() return def display_output_signal_info(self, frequencies): for channel_num in range( self.output_low_chan, self.output_high_chan + 1): curr_row = channel_num - self.output_low_chan self.freq_labels[curr_row]["text"] = str( frequencies[curr_row]) + " Hz" def add_output_example_data(self, data_array, ao_range, num_chans, rate, points_per_channel): # Calculate frequencies that will work well with the size of the array frequencies = [] for channel_num in range(0, num_chans): frequencies.append( (channel_num + 1) / (points_per_channel / rate)) # Calculate an amplitude and y-offset for the signal # to fill the analog output range amplitude = (ao_range.range_max - ao_range.range_min) / 2 y_offset = (amplitude + ao_range.range_min) / 2 # Fill the array with sine wave data at the calculated frequencies. # Note that since we are using the SCALEDATA option, the values # added to data_array are the actual voltage values that the device # will output data_index = 0 for point_num in range(0, points_per_channel): for channel_num in range(0, num_chans): freq = frequencies[channel_num] value = amplitude * math.sin( 2 * math.pi * freq * point_num / rate) + y_offset data_array[data_index] = value data_index += 1 return frequencies def update_output_displayed_values(self): # Get the status from the device status, curr_count, curr_index = ul.get_status(self.board_num, FunctionType.AOFUNCTION) # Display the status info self.update_output_status_labels(status, curr_count, curr_index) # Call this method again until the stop button is pressed if status == Status.RUNNING: self.after(100, self.update_output_displayed_values) else: # Free the allocated memory ul.win_buf_free(self.output_memhandle) self.set_output_ui_idle_state() def update_output_status_labels(self, status, curr_count, curr_index): if status == Status.IDLE: self.output_status_label["text"] = "Idle" else: self.output_status_label["text"] = "Running" self.output_index_label["text"] = str(curr_index) self.output_count_label["text"] = str(curr_count) def recreate_freq_frame(self): low_chan = self.output_low_chan high_chan = self.output_high_chan new_freq_frame = tk.Frame(self.freq_inner_frame) curr_row = 0 self.freq_labels = [] for chan_num in range(low_chan, high_chan + 1): curr_row += 1 channel_label = tk.Label(new_freq_frame) channel_label["text"] = ( "Channel " + str(chan_num) + " Frequency:") channel_label.grid(row=curr_row, column=0, sticky=tk.W) freq_label = tk.Label(new_freq_frame) freq_label.grid(row=curr_row, column=1, sticky=tk.W) self.freq_labels.append(freq_label) self.freq_frame.destroy() self.freq_frame = new_freq_frame self.freq_frame.grid() def stop_output(self): ul.stop_background(self.board_num, FunctionType.AOFUNCTION) def set_output_ui_idle_state(self): self.output_high_channel_entry["state"] = tk.NORMAL self.output_low_channel_entry["state"] = tk.NORMAL self.output_start_button["command"] = self.start_output self.output_start_button["text"] = "Start Analog Output" def start_output(self): self.output_high_channel_entry["state"] = tk.DISABLED self.output_low_channel_entry["state"] = tk.DISABLED self.output_start_button["command"] = self.stop_output self.output_start_button["text"] = "Stop Analog Output" self.start_output_scan() def stop_input(self): ul.stop_background(self.board_num, FunctionType.AIFUNCTION) def set_input_ui_idle_state(self): self.input_high_channel_entry["state"] = tk.NORMAL self.input_low_channel_entry["state"] = tk.NORMAL self.input_start_button["command"] = self.start_input self.input_start_button["text"] = "Start Analog Input" def start_input(self): self.input_high_channel_entry["state"] = tk.DISABLED self.input_low_channel_entry["state"] = tk.DISABLED self.input_start_button["command"] = self.stop_input self.input_start_button["text"] = "Stop Analog Input" self.start_input_scan() def get_input_low_channel_num(self): if self.ai_info.num_chans == 1: return 0 try: return int(self.input_low_channel_entry.get()) except ValueError: return 0 def get_input_high_channel_num(self): if self.ai_info.num_chans == 1: return 0 try: return int(self.input_high_channel_entry.get()) except ValueError: return 0 def get_output_low_channel_num(self): if self.ao_info.num_chans == 1: return 0 try: return int(self.output_low_channel_entry.get()) except ValueError: return 0 def get_output_high_channel_num(self): if self.ao_info.num_chans == 1: return 0 try: return int(self.output_high_channel_entry.get()) except ValueError: return 0 def validate_channel_entry(self, p): if p == '': return True try: value = int(p) if value < 0 or value > self.ai_info.num_chans - 1: return False except ValueError: return False return True def create_widgets(self): '''Create the tkinter UI''' self.device_label = tk.Label(self) self.device_label.pack(fill=tk.NONE, anchor=tk.NW) self.device_label["text"] = ('Board Number ' + str(self.board_num) + ": " + self.device_info.product_name + " (" + self.device_info.unique_id + ")") channel_vcmd = self.register(self.validate_channel_entry) main_frame = tk.Frame(self) main_frame.pack(fill=tk.X, anchor=tk.NW) input_groupbox = tk.LabelFrame(main_frame, text="Analog Input") input_groupbox.pack(side=tk.LEFT, anchor=tk.NW) if self.ai_info.num_chans > 1: curr_row = 0 input_channels_frame = tk.Frame(input_groupbox) input_channels_frame.pack(fill=tk.X, anchor=tk.NW) input_low_channel_entry_label = tk.Label( input_channels_frame) input_low_channel_entry_label["text"] = ( "Low Channel Number:") input_low_channel_entry_label.grid( row=curr_row, column=0, sticky=tk.W) self.input_low_channel_entry = tk.Spinbox( input_channels_frame, from_=0, to=max(self.ai_info.num_chans - 1, 0), validate='key', validatecommand=(channel_vcmd, '%P')) self.input_low_channel_entry.grid( row=curr_row, column=1, sticky=tk.W) curr_row += 1 input_high_channel_entry_label = tk.Label( input_channels_frame) input_high_channel_entry_label["text"] = ( "High Channel Number:") input_high_channel_entry_label.grid( row=curr_row, column=0, sticky=tk.W) self.input_high_channel_entry = tk.Spinbox( input_channels_frame, from_=0, to=max(self.ai_info.num_chans - 1, 0), validate='key', validatecommand=(channel_vcmd, '%P')) self.input_high_channel_entry.grid( row=curr_row, column=1, sticky=tk.W) initial_value = min(self.ai_info.num_chans - 1, 3) self.input_high_channel_entry.delete(0, tk.END) self.input_high_channel_entry.insert(0, str(initial_value)) curr_row += 1 self.input_start_button = tk.Button(input_groupbox) self.input_start_button["text"] = "Start Analog Input" self.input_start_button["command"] = self.start_input self.input_start_button.pack( fill=tk.X, anchor=tk.NW, padx=3, pady=3) self.input_results_group = tk.LabelFrame( input_groupbox, text="Results", padx=3, pady=3) self.input_results_group.pack( fill=tk.X, anchor=tk.NW, padx=3, pady=3) self.input_results_group.grid_columnconfigure(1, weight=1) curr_row = 0 input_status_left_label = tk.Label(self.input_results_group) input_status_left_label["text"] = "Status:" input_status_left_label.grid( row=curr_row, column=0, sticky=tk.W) self.input_status_label = tk.Label(self.input_results_group) self.input_status_label["text"] = "Idle" self.input_status_label.grid( row=curr_row, column=1, sticky=tk.W) curr_row += 1 input_index_left_label = tk.Label(self.input_results_group) input_index_left_label["text"] = "Index:" input_index_left_label.grid(row=curr_row, column=0, sticky=tk.W) self.input_index_label = tk.Label(self.input_results_group) self.input_index_label["text"] = "-1" self.input_index_label.grid(row=curr_row, column=1, sticky=tk.W) curr_row += 1 input_count_left_label = tk.Label(self.input_results_group) input_count_left_label["text"] = "Count:" input_count_left_label.grid(row=curr_row, column=0, sticky=tk.W) self.input_count_label = tk.Label(self.input_results_group) self.input_count_label["text"] = "0" self.input_count_label.grid(row=curr_row, column=1, sticky=tk.W) curr_row += 1 self.input_inner_data_frame = tk.Frame(self.input_results_group) self.input_inner_data_frame.grid( row=curr_row, column=0, columnspan=2, sticky=tk.W) self.data_frame = tk.Frame(self.input_inner_data_frame) self.data_frame.grid() output_groupbox = tk.LabelFrame( main_frame, text="Analog Output") output_groupbox.pack(side=tk.RIGHT, anchor=tk.NW) if self.ao_info.num_chans > 1: curr_row = 0 output_channels_frame = tk.Frame(output_groupbox) output_channels_frame.pack(fill=tk.X, anchor=tk.NW) output_low_channel_entry_label = tk.Label( output_channels_frame) output_low_channel_entry_label["text"] = ( "Low Channel Number:") output_low_channel_entry_label.grid( row=curr_row, column=0, sticky=tk.W) self.output_low_channel_entry = tk.Spinbox( output_channels_frame, from_=0, to=max(self.ao_info.num_chans - 1, 0), validate='key', validatecommand=(channel_vcmd, '%P')) self.output_low_channel_entry.grid( row=curr_row, column=1, sticky=tk.W) curr_row += 1 output_high_channel_entry_label = tk.Label( output_channels_frame) output_high_channel_entry_label["text"] = ( "High Channel Number:") output_high_channel_entry_label.grid( row=curr_row, column=0, sticky=tk.W) self.output_high_channel_entry = tk.Spinbox( output_channels_frame, from_=0, to=max(self.ao_info.num_chans - 1, 0), validate='key', validatecommand=(channel_vcmd, '%P')) self.output_high_channel_entry.grid( row=curr_row, column=1, sticky=tk.W) initial_value = min(self.ao_info.num_chans - 1, 3) self.output_high_channel_entry.delete(0, tk.END) self.output_high_channel_entry.insert(0, str(initial_value)) self.output_start_button = tk.Button(output_groupbox) self.output_start_button["text"] = "Start Analog Output" self.output_start_button["command"] = self.start_output self.output_start_button.pack( fill=tk.X, anchor=tk.NW, padx=3, pady=3) output_scan_info_group = tk.LabelFrame( output_groupbox, text="Scan Information", padx=3, pady=3) output_scan_info_group.pack( fill=tk.X, anchor=tk.NW, padx=3, pady=3) output_scan_info_group.grid_columnconfigure(1, weight=1) curr_row = 0 output_status_left_label = tk.Label(output_scan_info_group) output_status_left_label["text"] = "Status:" output_status_left_label.grid( row=curr_row, column=0, sticky=tk.W) self.output_status_label = tk.Label(output_scan_info_group) self.output_status_label["text"] = "Idle" self.output_status_label.grid( row=curr_row, column=1, sticky=tk.W) curr_row += 1 output_index_left_label = tk.Label(output_scan_info_group) output_index_left_label["text"] = "Index:" output_index_left_label.grid( row=curr_row, column=0, sticky=tk.W) self.output_index_label = tk.Label(output_scan_info_group) self.output_index_label["text"] = "-1" self.output_index_label.grid( row=curr_row, column=1, sticky=tk.W) curr_row += 1 output_count_left_label = tk.Label(output_scan_info_group) output_count_left_label["text"] = "Count:" output_count_left_label.grid( row=curr_row, column=0, sticky=tk.W) self.output_count_label = tk.Label(output_scan_info_group) self.output_count_label["text"] = "0" self.output_count_label.grid( row=curr_row, column=1, sticky=tk.W) curr_row += 1 self.freq_inner_frame = tk.Frame(output_scan_info_group) self.freq_inner_frame.grid( row=curr_row, column=0, columnspan=2, sticky=tk.W) self.freq_frame = tk.Frame(self.freq_inner_frame) self.freq_frame.grid() button_frame = tk.Frame(self) button_frame.pack(fill=tk.X, side=tk.RIGHT, anchor=tk.SE) self.quit_button = tk.Button(button_frame) self.quit_button["text"] = "Quit" self.quit_button["command"] = self.exit self.quit_button.grid(row=0, column=1, padx=3, pady=3)
class ULAI01(UIExample): def __init__(self, master): super(ULAI01, self).__init__(master) # By default, the example detects all available devices and selects the # first device listed. # If use_device_detection is set to False, the board_num property needs # to match the desired board number configured with Instacal. use_device_detection = True self.board_num = 0 self.running = False try: if use_device_detection: self.configure_first_detected_device() self.device_info = DaqDeviceInfo(self.board_num) if self.device_info.supports_analog_input: self.ai_info = self.device_info.get_ai_info() self.create_widgets() else: self.create_unsupported_widgets() except ULError: self.create_unsupported_widgets(True) def update_value(self): channel = self.get_channel_num() ai_range = self.ai_info.supported_ranges[0] try: # Get a value from the device if self.ai_info.resolution <= 16: # Use the a_in method for devices with a resolution <= 16 value = ul.a_in(self.board_num, channel, ai_range) # Convert the raw value to engineering units eng_units_value = ul.to_eng_units(self.board_num, ai_range, value) else: # Use the a_in_32 method for devices with a resolution > 16 # (optional parameter omitted) value = ul.a_in_32(self.board_num, channel, ai_range) # Convert the raw value to engineering units eng_units_value = ul.to_eng_units_32(self.board_num, ai_range, value) # Display the raw value self.value_label["text"] = str(value) # Display the engineering value self.eng_value_label["text"] = '{:.3f}'.format(eng_units_value) # Call this method again until the stop button is pressed (or an # error occurs) if self.running: self.after(100, self.update_value) except ULError as e: self.stop() show_ul_error(e) def stop(self): self.running = False self.start_button["command"] = self.start self.start_button["text"] = "Start" def start(self): self.running = True self.start_button["command"] = self.stop self.start_button["text"] = "Stop" self.update_value() def get_channel_num(self): if self.ai_info.num_chans == 1: return 0 try: return int(self.channel_entry.get()) except ValueError: return 0 def validate_channel_entry(self, p): if p == '': return True try: value = int(p) if value < 0 or value > self.ai_info.num_chans - 1: return False except ValueError: return False return True def create_widgets(self): '''Create the tkinter UI''' self.device_label = tk.Label(self) self.device_label.pack(fill=tk.NONE, anchor=tk.NW) self.device_label["text"] = ('Board Number ' + str(self.board_num) + ": " + self.device_info.product_name + " (" + self.device_info.unique_id + ")") main_frame = tk.Frame(self) main_frame.pack(fill=tk.X, anchor=tk.NW) curr_row = 0 if self.ai_info.num_chans > 1: channel_vcmd = self.register(self.validate_channel_entry) channel_entry_label = tk.Label(main_frame) channel_entry_label["text"] = "Channel Number:" channel_entry_label.grid( row=curr_row, column=0, sticky=tk.W) self.channel_entry = tk.Spinbox( main_frame, from_=0, to=max(self.ai_info.num_chans - 1, 0), validate='key', validatecommand=(channel_vcmd, '%P')) self.channel_entry.grid( row=curr_row, column=1, sticky=tk.W) curr_row += 1 raw_value_left_label = tk.Label(main_frame) raw_value_left_label["text"] = "Value read from selected channel:" raw_value_left_label.grid(row=curr_row, column=0, sticky=tk.W) self.value_label = tk.Label(main_frame) self.value_label.grid(row=curr_row, column=1, sticky=tk.W) curr_row += 1 eng_value_left_label = tk.Label(main_frame) eng_value_left_label["text"] = "Value converted to voltage:" eng_value_left_label.grid(row=curr_row, column=0, sticky=tk.W) self.eng_value_label = tk.Label(main_frame) self.eng_value_label.grid(row=curr_row, column=1, sticky=tk.W) button_frame = tk.Frame(self) button_frame.pack(fill=tk.X, side=tk.RIGHT, anchor=tk.SE) self.start_button = tk.Button(button_frame) self.start_button["text"] = "Start" self.start_button["command"] = self.start self.start_button.grid(row=0, column=0, padx=3, pady=3) quit_button = tk.Button(button_frame) quit_button["text"] = "Quit" quit_button["command"] = self.master.destroy quit_button.grid(row=0, column=1, padx=3, pady=3)
def run_example(): # By default, the example detects and displays all available devices and # selects the first device listed. Use the dev_id_list variable to filter # detected devices by device ID (see UL documentation for device IDs). # If use_device_detection is set to False, the board_num variable needs to # match the desired board number configured with Instacal. use_device_detection = True dev_id_list = [] board_num = 0 rate = 100 file_name = 'scan_data.csv' memhandle = None # The size of the UL buffer to create, in seconds buffer_size_seconds = 2 # The number of buffers to write. After this number of UL buffers are # written to file, the example will be stopped. num_buffers_to_write = 5 try: if use_device_detection: config_first_detected_device(board_num, dev_id_list) daq_dev_info = DaqDeviceInfo(board_num) if not daq_dev_info.supports_analog_input: raise Exception('Error: The DAQ device does not support ' 'analog input') print('\nActive DAQ device: ', daq_dev_info.product_name, ' (', daq_dev_info.unique_id, ')\n', sep='') ai_info = daq_dev_info.get_ai_info() low_chan = 0 high_chan = min(3, ai_info.num_chans - 1) num_chans = high_chan - low_chan + 1 # Create a circular buffer that can hold buffer_size_seconds worth of # data, or at least 10 points (this may need to be adjusted to prevent # a buffer overrun) points_per_channel = max(rate * buffer_size_seconds, 10) # Some hardware requires that the total_count is an integer multiple # of the packet size. For this case, calculate a points_per_channel # that is equal to or just above the points_per_channel selected # which matches that requirement. if ai_info.packet_size != 1: packet_size = ai_info.packet_size remainder = points_per_channel % packet_size if remainder != 0: points_per_channel += packet_size - remainder ul_buffer_count = points_per_channel * num_chans # Write the UL buffer to the file num_buffers_to_write times. points_to_write = ul_buffer_count * num_buffers_to_write # When handling the buffer, we will read 1/10 of the buffer at a time write_chunk_size = int(ul_buffer_count / 10) ai_range = ai_info.supported_ranges[0] scan_options = (ScanOptions.BACKGROUND | ScanOptions.CONTINUOUS | ScanOptions.SCALEDATA) memhandle = ul.scaled_win_buf_alloc(ul_buffer_count) # Allocate an array of doubles temporary storage of the data write_chunk_array = (c_double * write_chunk_size)() # Check if the buffer was successfully allocated if not memhandle: raise Exception('Failed to allocate memory') # Start the scan ul.a_in_scan( board_num, low_chan, high_chan, ul_buffer_count, rate, ai_range, memhandle, scan_options) status = Status.IDLE # Wait for the scan to start fully while status == Status.IDLE: status, _, _ = ul.get_status(board_num, FunctionType.AIFUNCTION) # Create a file for storing the data with open(file_name, 'w') as f: print('Writing data to ' + file_name, end='') # Write a header to the file for chan_num in range(low_chan, high_chan + 1): f.write('Channel ' + str(chan_num) + ',') f.write(u'\n') # Start the write loop prev_count = 0 prev_index = 0 write_ch_num = low_chan while status != Status.IDLE: # Get the latest counts status, curr_count, _ = ul.get_status(board_num, FunctionType.AIFUNCTION) new_data_count = curr_count - prev_count # Check for a buffer overrun before copying the data, so # that no attempts are made to copy more than a full buffer # of data if new_data_count > ul_buffer_count: # Print an error and stop writing ul.stop_background(board_num, FunctionType.AIFUNCTION) print('A buffer overrun occurred') break # Check if a chunk is available if new_data_count > write_chunk_size: wrote_chunk = True # Copy the current data to a new array # Check if the data wraps around the end of the UL # buffer. Multiple copy operations will be required. if prev_index + write_chunk_size > ul_buffer_count - 1: first_chunk_size = ul_buffer_count - prev_index second_chunk_size = ( write_chunk_size - first_chunk_size) # Copy the first chunk of data to the # write_chunk_array ul.scaled_win_buf_to_array( memhandle, write_chunk_array, prev_index, first_chunk_size) # Create a pointer to the location in # write_chunk_array where we want to copy the # remaining data second_chunk_pointer = cast(addressof(write_chunk_array) + first_chunk_size * sizeof(c_double), POINTER(c_double)) # Copy the second chunk of data to the # write_chunk_array ul.scaled_win_buf_to_array( memhandle, second_chunk_pointer, 0, second_chunk_size) else: # Copy the data to the write_chunk_array ul.scaled_win_buf_to_array( memhandle, write_chunk_array, prev_index, write_chunk_size) # Check for a buffer overrun just after copying the data # from the UL buffer. This will ensure that the data was # not overwritten in the UL buffer before the copy was # completed. This should be done before writing to the # file, so that corrupt data does not end up in it. status, curr_count, _ = ul.get_status( board_num, FunctionType.AIFUNCTION) if curr_count - prev_count > ul_buffer_count: # Print an error and stop writing ul.stop_background(board_num, FunctionType.AIFUNCTION) print('A buffer overrun occurred') break for i in range(write_chunk_size): f.write(str(write_chunk_array[i]) + ',') write_ch_num += 1 if write_ch_num == high_chan + 1: write_ch_num = low_chan f.write(u'\n') else: wrote_chunk = False if wrote_chunk: # Increment prev_count by the chunk size prev_count += write_chunk_size # Increment prev_index by the chunk size prev_index += write_chunk_size # Wrap prev_index to the size of the UL buffer prev_index %= ul_buffer_count if prev_count >= points_to_write: break print('.', end='') else: # Wait a short amount of time for more data to be # acquired. sleep(0.1) ul.stop_background(board_num, FunctionType.AIFUNCTION) except Exception as e: print('\n', e) finally: print('Done') if memhandle: # Free the buffer in a finally block to prevent a memory leak. ul.win_buf_free(memhandle) if use_device_detection: ul.release_daq_device(board_num)
class ULTI02(UIExample): def __init__(self, master): super(ULTI02, self).__init__(master) # By default, the example detects all available devices and selects the # first device listed. # If use_device_detection is set to False, the board_num property needs # to match the desired board number configured with Instacal. use_device_detection = True self.board_num = 0 self.running = False try: if use_device_detection: self.configure_first_detected_device() self.device_info = DaqDeviceInfo(self.board_num) self.ai_info = self.device_info.get_ai_info() if self.ai_info.temp_supported: self.create_widgets() else: self.create_unsupported_widgets() except ULError: self.create_unsupported_widgets(True) def update_values(self): try: # Get the values from the device (optional parameters omitted) err_code, data_array = ul.t_in_scan(self.board_num, self.low_chan, self.high_chan, TempScale.CELSIUS) # Check err_code for OUTOFRANGE or OPENCONNECTION. All other # error codes will raise a ULError and are checked by the except # clause. if err_code == ErrorCode.OUTOFRANGE: self.warning_label["text"] = ( "A thermocouple input is out of range.") elif err_code == ErrorCode.OPENCONNECTION: self.warning_label["text"] = ( "A thermocouple input has an open connection.") else: self.warning_label["text"] = "" self.display_values(data_array) # Call this method again until the stop button is pressed (or an # error occurs) if self.running: self.after(100, self.update_values) except ULError as e: self.stop() show_ul_error(e) def display_values(self, array): low_chan = self.low_chan high_chan = self.high_chan for chan_num in range(low_chan, high_chan + 1): index = chan_num - low_chan self.data_labels[index]["text"] = '{:.3f}'.format( array[index]) + "\n" def stop(self): self.running = False self.start_button["command"] = self.start self.start_button["text"] = "Start" self.low_channel_entry["state"] = tk.NORMAL self.high_channel_entry["state"] = tk.NORMAL def start(self): self.running = True self.start_button["command"] = self.stop self.start_button["text"] = "Stop" self.low_channel_entry["state"] = tk.DISABLED self.high_channel_entry["state"] = tk.DISABLED self.low_chan = self.get_low_channel_num() self.high_chan = self.get_high_channel_num() self.recreate_data_frame() self.update_values() def get_low_channel_num(self): try: return int(self.low_channel_entry.get()) except ValueError: return 0 def get_high_channel_num(self): try: return int(self.high_channel_entry.get()) except ValueError: return 0 def validate_channel_entry(self, p): if p == '': return True try: value = int(p) if value < 0 or value > self.ai_info.num_temp_chans - 1: return False except ValueError: return False return True def recreate_data_frame(self): low_chan = self.low_chan high_chan = self.high_chan channels_per_row = 4 new_data_frame = tk.Frame(self.results_group) self.data_labels = [] row = 0 column = 0 # Add the labels for each channel for chan_num in range(low_chan, high_chan + 1): chan_label = tk.Label(new_data_frame, justify=tk.LEFT, padx=3) chan_label["text"] = "Channel " + str(chan_num) chan_label.grid(row=row, column=column) data_label = tk.Label(new_data_frame, justify=tk.LEFT, padx=3) data_label.grid(row=row + 1, column=column) self.data_labels.append(data_label) column += 1 if column >= channels_per_row: row += 2 column = 0 self.data_frame.destroy() self.data_frame = new_data_frame self.data_frame.pack(side=tk.TOP) def create_widgets(self): '''Create the tkinter UI''' self.device_label = tk.Label(self) self.device_label.pack(fill=tk.NONE, anchor=tk.NW) self.device_label["text"] = ('Board Number ' + str(self.board_num) + ": " + self.device_info.product_name + " (" + self.device_info.unique_id + ")") main_frame = tk.Frame(self) main_frame.pack(fill=tk.X, anchor=tk.NW) channel_vcmd = self.register(self.validate_channel_entry) curr_row = 0 if self.ai_info.num_temp_chans > 1: low_channel_entry_label = tk.Label(main_frame) low_channel_entry_label["text"] = "Low Channel Number:" low_channel_entry_label.grid(row=curr_row, column=0, sticky=tk.W) self.low_channel_entry = tk.Spinbox( main_frame, from_=0, to=max(self.ai_info.num_temp_chans - 1, 0), validate='key', validatecommand=(channel_vcmd, '%P')) self.low_channel_entry.grid(row=curr_row, column=1, sticky=tk.W) curr_row += 1 high_channel_entry_label = tk.Label(main_frame) high_channel_entry_label["text"] = "High Channel Number:" high_channel_entry_label.grid(row=curr_row, column=0, sticky=tk.W) self.high_channel_entry = tk.Spinbox( main_frame, from_=0, to=max(self.ai_info.num_temp_chans - 1, 0), validate='key', validatecommand=(channel_vcmd, '%P')) self.high_channel_entry.grid(row=curr_row, column=1, sticky=tk.W) initial_value = min(self.ai_info.num_temp_chans - 1, 3) self.high_channel_entry.delete(0, tk.END) self.high_channel_entry.insert(0, str(initial_value)) self.results_group = tk.LabelFrame(self, text="Results") self.results_group.pack(fill=tk.X, anchor=tk.NW, padx=3, pady=3) self.data_frame = tk.Frame(self.results_group) self.data_frame.pack(side=tk.TOP) self.warning_label = tk.Label(self.results_group, fg="red") self.warning_label.pack(side=tk.BOTTOM) button_frame = tk.Frame(self) button_frame.pack(fill=tk.X, side=tk.RIGHT, anchor=tk.SE) button_frame = tk.Frame(self) button_frame.pack(fill=tk.X, side=tk.RIGHT, anchor=tk.SE) self.start_button = tk.Button(button_frame) self.start_button["text"] = "Start" self.start_button["command"] = self.start self.start_button.grid(row=0, column=0, padx=3, pady=3) quit_button = tk.Button(button_frame) quit_button["text"] = "Quit" quit_button["command"] = self.master.destroy quit_button.grid(row=0, column=1, padx=3, pady=3)
class DaqInScan02(UIExample): def __init__(self, master=None): super(DaqInScan02, self).__init__(master) # By default, the example detects all available devices and selects the # first device listed. # If use_device_detection is set to False, the board_num property needs # to match the desired board number configured with Instacal. use_device_detection = True self.board_num = 0 self.chan_list = [] self.chan_type_list = [] self.gain_list = [] self.num_chans = 0 self.resolution = 16 try: if use_device_detection: self.configure_first_detected_device() self.device_info = DaqDeviceInfo(self.board_num) if self.device_info.supports_daq_input: self.init_scan_channel_info() self.create_widgets() else: self.create_unsupported_widgets() except ULError: self.create_unsupported_widgets(True) def init_scan_channel_info(self): num_channels = 0 daqi_info = self.device_info.get_daqi_info() supported_channel_types = daqi_info.supported_channel_types # Add analog input channels if available if ChannelType.ANALOG in supported_channel_types: ai_info = self.device_info.get_ai_info() self.resolution = ai_info.resolution self.chan_list.append(0) self.chan_type_list.append(ChannelType.ANALOG) self.gain_list.append(ai_info.supported_ranges[0]) num_channels += 1 if ai_info.num_chans > 1: self.chan_list.append(ai_info.num_chans - 1) self.chan_type_list.append(ChannelType.ANALOG) self.gain_list.append(ai_info.supported_ranges[0]) num_channels += 1 # Add a digital input channel if available if self.device_info.supports_digital_io: chan_type = None if ChannelType.DIGITAL16 in supported_channel_types: chan_type = ChannelType.DIGITAL16 elif ChannelType.DIGITAL8 in supported_channel_types: chan_type = ChannelType.DIGITAL8 elif ChannelType.DIGITAL in supported_channel_types: chan_type = ChannelType.DIGITAL if chan_type is not None: dio_info = self.device_info.get_dio_info() port_info = dio_info.port_info[0] self.chan_list.append(port_info.type) self.chan_type_list.append(chan_type) self.gain_list.append(ULRange.NOTUSED) num_channels += 1 # Configure all digital ports for input for port in dio_info.port_info: if port.is_port_configurable: ul.d_config_port(self.board_num, port.type, DigitalIODirection.IN) if self.device_info.supports_counters: chan_type = None if ChannelType.CTR16 in supported_channel_types: chan_type = ChannelType.CTR16 elif ChannelType.CTRBANK0 in supported_channel_types: chan_type = ChannelType.CTRBANK0 elif ChannelType.CTR in supported_channel_types: chan_type = ChannelType.CTR if chan_type is not None: self.chan_list.append(0) self.chan_type_list.append(chan_type) self.gain_list.append(ULRange.NOTUSED) num_channels += 1 self.num_chans = num_channels def start_scan(self): rate = 100 points_per_channel = 100 total_count = points_per_channel * self.num_chans scan_options = ScanOptions.BACKGROUND | ScanOptions.CONTINUOUS # Allocate a buffer for the scan if self.resolution <= 16: self.memhandle = ul.win_buf_alloc(total_count) else: self.memhandle = ul.win_buf_alloc_32(total_count) # Check if the buffer was successfully allocated if not self.memhandle: messagebox.showerror("Error", "Failed to allocate memory") self.start_button["state"] = tk.NORMAL return try: # Run the scan ul.daq_in_scan(self.board_num, self.chan_list, self.chan_type_list, self.gain_list, self.num_chans, rate, 0, total_count, self.memhandle, scan_options) # Cast the memhandle to a ctypes pointer # Note: the ctypes array will only be valid until win_buf_free # is called. # A copy of the buffer can be created using win_buf_to_array # or win_buf_to_array_32 before the memory is freed. The copy can # be used at any time. if self.resolution <= 16: # Use the memhandle_as_ctypes_array method for devices with a # resolution <= 16 self.array = cast(self.memhandle, POINTER(c_ushort)) else: # Use the memhandle_as_ctypes_array_32 method for devices with a # resolution > 16 self.array = cast(self.memhandle, POINTER(c_ulong)) except ULError as e: # Free the allocated memory ul.win_buf_free(self.memhandle) show_ul_error(e) return # Start updating the displayed values self.update_displayed_values() def update_displayed_values(self): # Get the status from the device status, curr_count, curr_index = ul.get_status( self.board_num, FunctionType.DAQIFUNCTION) # Display the status info self.update_status_labels(status, curr_count, curr_index) # Display the values self.display_values(curr_index, curr_count) # Call this method again until the stop button is pressed if status == Status.RUNNING: self.after(100, self.update_displayed_values) else: # Free the allocated memory ul.win_buf_free(self.memhandle) self.set_ui_idle_state() def update_status_labels(self, status, curr_count, curr_index): if status == Status.IDLE: self.status_label["text"] = "Idle" else: self.status_label["text"] = "Running" self.index_label["text"] = str(curr_index) self.count_label["text"] = str(curr_count) def display_values(self, curr_index, curr_count): per_channel_display_count = 10 array = self.array chan_count = self.num_chans channel_text = [] # Add a string to the array for each channel for _ in range(0, self.num_chans): channel_text.append("") # If no data has been gathered, don't add data to the labels if curr_count > 1: # curr_index points to the start of the last completed channel scan # that was transferred between the board and the data buffer. Based # on this, calculate the first index we want to display using # subtraction. first_index = max( curr_index - ((per_channel_display_count - 1) * chan_count), 0) chan_num = 0 # Add (up to) the latest 10 values for each channel to the text for data_index in range( first_index, first_index + min(chan_count * per_channel_display_count, curr_count)): channel_text[chan_num] += str(array[data_index]) + "\n" if chan_num == self.num_chans - 1: chan_num = 0 else: chan_num += 1 # Update the labels for each channel for chan_num in range(0, self.num_chans): self.data_labels[chan_num]["text"] = channel_text[chan_num] def stop(self): ul.stop_background(self.board_num, FunctionType.DAQIFUNCTION) def set_ui_idle_state(self): self.start_button["command"] = self.start self.start_button["text"] = "Start" def start(self): self.start_button["command"] = self.stop self.start_button["text"] = "Stop" self.start_scan() def create_widgets(self): '''Create the tkinter UI''' self.device_label = tk.Label(self) self.device_label.pack(fill=tk.NONE, anchor=tk.NW) self.device_label["text"] = ('Board Number ' + str(self.board_num) + ": " + self.device_info.product_name + " (" + self.device_info.unique_id + ")") main_frame = tk.Frame(self) main_frame.pack(fill=tk.X, anchor=tk.NW) self.results_group = tk.LabelFrame( self, text="Results", padx=3, pady=3) self.results_group.pack(fill=tk.X, anchor=tk.NW, padx=3, pady=3) curr_row = 0 status_left_label = tk.Label(self.results_group) status_left_label["text"] = "Status:" status_left_label.grid(row=curr_row, column=0, sticky=tk.W) self.status_label = tk.Label(self.results_group) self.status_label["text"] = "Idle" self.status_label.grid(row=curr_row, column=1, sticky=tk.W) curr_row += 1 index_left_label = tk.Label(self.results_group) index_left_label["text"] = "Index:" index_left_label.grid(row=curr_row, column=0, sticky=tk.W) self.index_label = tk.Label(self.results_group) self.index_label["text"] = "-1" self.index_label.grid(row=curr_row, column=1, sticky=tk.W) curr_row += 1 count_left_label = tk.Label(self.results_group) count_left_label["text"] = "Count:" count_left_label.grid(row=curr_row, column=0, sticky=tk.W) self.count_label = tk.Label(self.results_group) self.count_label["text"] = "0" self.count_label.grid(row=curr_row, column=1, sticky=tk.W) curr_row += 1 self.data_frame = tk.Frame(self.results_group) self.data_frame.grid(row=curr_row, column=0, columnspan=2, sticky=tk.W) chan_header_label = tk.Label( self.data_frame, justify=tk.LEFT, padx=3) chan_header_label["text"] = "Channel:" chan_header_label.grid(row=0, column=0) type_header_label = tk.Label( self.data_frame, justify=tk.LEFT, padx=3) type_header_label["text"] = "Type:" type_header_label.grid(row=1, column=0) range_header_label = tk.Label( self.data_frame, justify=tk.LEFT, padx=3) range_header_label["text"] = "Range:" range_header_label.grid(row=2, column=0) self.data_labels = [] for chan_num in range(0, self.num_chans): column = chan_num + 1 chan_label = tk.Label(self.data_frame, justify=tk.LEFT, padx=3) chan_num_item = self.chan_list[chan_num] if isinstance(chan_num_item, Enum): chan_label["text"] = self.chan_list[chan_num].name else: chan_label["text"] = str(self.chan_list[chan_num]) chan_label.grid(row=0, column=column) type_label = tk.Label(self.data_frame, justify=tk.LEFT, padx=3) type_label["text"] = self.chan_type_list[chan_num].name type_label.grid(row=1, column=column) range_label = tk.Label( self.data_frame, justify=tk.LEFT, padx=3) range_label["text"] = self.gain_list[chan_num].name range_label.grid(row=2, column=column) data_label = tk.Label(self.data_frame, justify=tk.LEFT, padx=3) data_label.grid(row=3, column=column) self.data_labels.append(data_label) button_frame = tk.Frame(self) button_frame.pack(fill=tk.X, side=tk.RIGHT, anchor=tk.SE) self.start_button = tk.Button(button_frame) self.start_button["text"] = "Start" self.start_button["command"] = self.start self.start_button.grid(row=0, column=0, padx=3, pady=3) quit_button = tk.Button(button_frame) quit_button["text"] = "Quit" quit_button["command"] = self.master.destroy quit_button.grid(row=0, column=1, padx=3, pady=3)
def run_example(): # By default, the example detects and displays all available devices and # selects the first device listed. Use the dev_id_list variable to filter # detected devices by device ID (see UL documentation for device IDs). # If use_device_detection is set to False, the board_num variable needs to # match the desired board number configured with Instacal. use_device_detection = True dev_id_list = [] board_num = 0 rate = 100 points_per_channel = 10 memhandle = None try: if use_device_detection: config_first_detected_device(board_num, dev_id_list) daq_dev_info = DaqDeviceInfo(board_num) if not daq_dev_info.supports_analog_input: raise Exception('Error: The DAQ device does not support ' 'analog input') print('\nActive DAQ device: ', daq_dev_info.product_name, ' (', daq_dev_info.unique_id, ')\n', sep='') ai_info = daq_dev_info.get_ai_info() low_chan = 0 high_chan = min(3, ai_info.num_chans - 1) num_chans = high_chan - low_chan + 1 total_count = points_per_channel * num_chans ai_range = ai_info.supported_ranges[0] scan_options = ScanOptions.FOREGROUND if ScanOptions.SCALEDATA in ai_info.supported_scan_options: # If the hardware supports the SCALEDATA option, it is easiest to # use it. scan_options |= ScanOptions.SCALEDATA memhandle = ul.scaled_win_buf_alloc(total_count) # Convert the memhandle to a ctypes array. # Use the memhandle_as_ctypes_array_scaled method for scaled # buffers. ctypes_array = cast(memhandle, POINTER(c_double)) elif ai_info.resolution <= 16: # Use the win_buf_alloc method for devices with a resolution <= 16 memhandle = ul.win_buf_alloc(total_count) # Convert the memhandle to a ctypes array. # Use the memhandle_as_ctypes_array method for devices with a # resolution <= 16. ctypes_array = cast(memhandle, POINTER(c_ushort)) else: # Use the win_buf_alloc_32 method for devices with a resolution > 16 memhandle = ul.win_buf_alloc_32(total_count) # Convert the memhandle to a ctypes array. # Use the memhandle_as_ctypes_array_32 method for devices with a # resolution > 16 ctypes_array = cast(memhandle, POINTER(c_ulong)) # Note: the ctypes array will no longer be valid after win_buf_free is # called. # A copy of the buffer can be created using win_buf_to_array or # win_buf_to_array_32 before the memory is freed. The copy can be used # at any time. # Check if the buffer was successfully allocated if not memhandle: raise Exception('Error: Failed to allocate memory') # Start the scan ul.a_in_scan(board_num, low_chan, high_chan, total_count, rate, ai_range, memhandle, scan_options) print('Scan completed successfully. Data:') # Create a format string that aligns the data in columns row_format = '{:>5}' + '{:>10}' * num_chans # Print the channel name headers labels = ['Index'] for ch_num in range(low_chan, high_chan + 1): labels.append('CH' + str(ch_num)) print(row_format.format(*labels)) # Print the data data_index = 0 for index in range(points_per_channel): display_data = [index] for _ in range(num_chans): if ScanOptions.SCALEDATA in scan_options: # If the SCALEDATA ScanOption was used, the values # in the array are already in engineering units. eng_value = ctypes_array[data_index] else: # If the SCALEDATA ScanOption was NOT used, the # values in the array must be converted to # engineering units using ul.to_eng_units(). eng_value = ul.to_eng_units(board_num, ai_range, ctypes_array[data_index]) data_index += 1 display_data.append('{:.3f}'.format(eng_value)) # Print this row print(row_format.format(*display_data)) except Exception as e: print('\n', e) finally: if memhandle: # Free the buffer in a finally block to prevent a memory leak. ul.win_buf_free(memhandle) if use_device_detection: ul.release_daq_device(board_num)
class DaqInScan01(UIExample): def __init__(self, master=None): super(DaqInScan01, self).__init__(master) # By default, the example detects all available devices and selects the # first device listed. # If use_device_detection is set to False, the board_num property needs # to match the desired board number configured with Instacal. use_device_detection = True self.board_num = 0 self.chan_list = [] self.chan_type_list = [] self.gain_list = [] self.num_chans = 0 self.resolution = 16 try: if use_device_detection: self.configure_first_detected_device() self.device_info = DaqDeviceInfo(self.board_num) if self.device_info.supports_daq_input: self.init_scan_channel_info() self.create_widgets() else: self.create_unsupported_widgets() except ULError: self.create_unsupported_widgets(True) def init_scan_channel_info(self): num_channels = 0 daqi_info = self.device_info.get_daqi_info() supported_channel_types = daqi_info.supported_channel_types # Add analog input channels if available if ChannelType.ANALOG in supported_channel_types: ai_info = self.device_info.get_ai_info() self.resolution = ai_info.resolution self.chan_list.append(0) self.chan_type_list.append(ChannelType.ANALOG) self.gain_list.append(ai_info.supported_ranges[0]) num_channels += 1 ai_info = self.device_info.get_ai_info() if ai_info.num_chans > 1: self.chan_list.append(ai_info.num_chans - 1) self.chan_type_list.append(ChannelType.ANALOG) self.gain_list.append(ai_info.supported_ranges[0]) num_channels += 1 # Add a digital input channel if available if self.device_info.supports_digital_io: chan_type = None if ChannelType.DIGITAL16 in supported_channel_types: chan_type = ChannelType.DIGITAL16 elif ChannelType.DIGITAL8 in supported_channel_types: chan_type = ChannelType.DIGITAL8 elif ChannelType.DIGITAL in supported_channel_types: chan_type = ChannelType.DIGITAL if chan_type is not None: dio_info = self.device_info.get_dio_info() port_info = dio_info.port_info[0] self.chan_list.append(port_info.type) self.chan_type_list.append(chan_type) self.gain_list.append(ULRange.NOTUSED) num_channels += 1 # Configure all digital ports for input for port in dio_info.port_info: if port.is_port_configurable: ul.d_config_port(self.board_num, port.type, DigitalIODirection.IN) if self.device_info.supports_counters: chan_type = None if ChannelType.CTR16 in supported_channel_types: chan_type = ChannelType.CTR16 elif ChannelType.CTRBANK0 in supported_channel_types: chan_type = ChannelType.CTRBANK0 elif ChannelType.CTR in supported_channel_types: chan_type = ChannelType.CTR if chan_type is not None: self.chan_list.append(0) self.chan_type_list.append(chan_type) self.gain_list.append(ULRange.NOTUSED) num_channels += 1 self.num_chans = num_channels def start_scan(self): rate = 100 points_per_channel = 10 total_count = points_per_channel * self.num_chans # Allocate a buffer for the scan if self.resolution <= 16: memhandle = ul.win_buf_alloc(total_count) else: memhandle = ul.win_buf_alloc_32(total_count) # Check if the buffer was successfully allocated if not memhandle: messagebox.showerror("Error", "Failed to allocate memory") self.start_button["state"] = tk.NORMAL return try: # Run the scan ul.daq_in_scan(self.board_num, self.chan_list, self.chan_type_list, self.gain_list, self.num_chans, rate, 0, total_count, memhandle, 0) # Cast the memhandle to a ctypes pointer # Note: the ctypes array will only be valid until win_buf_free # is called. # A copy of the buffer can be created using win_buf_to_array # or win_buf_to_array_32 before the memory is freed. The copy can # be used at any time. if self.resolution <= 16: # Use the memhandle_as_ctypes_array method for devices with a # resolution <= 16 array = cast(memhandle, POINTER(c_ushort)) else: # Use the memhandle_as_ctypes_array_32 method for devices with # a resolution > 16 array = cast(memhandle, POINTER(c_ulong)) # Display the values self.display_values(array, total_count) except ULError as e: show_ul_error(e) finally: # Free the allocated memory ul.win_buf_free(memhandle) self.start_button["state"] = tk.NORMAL def display_values(self, array, total_count): channel_text = [] # Add a string to the array for each channel for _ in range(0, self.num_chans): channel_text.append("") # Add (up to) the first 10 values for each channel to the text chan_num = 0 for data_index in range(0, min(self.num_chans * 10, total_count)): channel_text[chan_num] += str(array[data_index]) + "\n" if chan_num == self.num_chans - 1: chan_num = 0 else: chan_num += 1 # Update the labels for each channel for chan_num in range(0, self.num_chans): self.data_labels[chan_num]["text"] = channel_text[chan_num] def start(self): self.start_button["state"] = tk.DISABLED self.start_scan() def create_widgets(self): '''Create the tkinter UI''' self.device_label = tk.Label(self) self.device_label.pack(fill=tk.NONE, anchor=tk.NW) self.device_label["text"] = ('Board Number ' + str(self.board_num) + ": " + self.device_info.product_name + " (" + self.device_info.unique_id + ")") main_frame = tk.Frame(self) main_frame.pack(fill=tk.X, anchor=tk.NW) self.results_group = tk.LabelFrame(self, text="Results", padx=3, pady=3) self.results_group.pack(fill=tk.X, anchor=tk.NW, padx=3, pady=3) self.data_frame = tk.Frame(self.results_group) self.data_frame.grid() chan_header_label = tk.Label(self.data_frame, justify=tk.LEFT, padx=3) chan_header_label["text"] = "Channel:" chan_header_label.grid(row=0, column=0) type_header_label = tk.Label(self.data_frame, justify=tk.LEFT, padx=3) type_header_label["text"] = "Type:" type_header_label.grid(row=1, column=0) range_header_label = tk.Label(self.data_frame, justify=tk.LEFT, padx=3) range_header_label["text"] = "Range:" range_header_label.grid(row=2, column=0) self.data_labels = [] for chan_num in range(0, self.num_chans): column = chan_num + 1 chan_label = tk.Label(self.data_frame, justify=tk.LEFT, padx=3) chan_num_item = self.chan_list[chan_num] if isinstance(chan_num_item, Enum): chan_label["text"] = self.chan_list[chan_num].name else: chan_label["text"] = str(self.chan_list[chan_num]) chan_label.grid(row=0, column=column) type_label = tk.Label(self.data_frame, justify=tk.LEFT, padx=3) type_label["text"] = self.chan_type_list[chan_num].name type_label.grid(row=1, column=column) range_label = tk.Label(self.data_frame, justify=tk.LEFT, padx=3) range_label["text"] = self.gain_list[chan_num].name range_label.grid(row=2, column=column) data_label = tk.Label(self.data_frame, justify=tk.LEFT, padx=3) data_label.grid(row=3, column=column) self.data_labels.append(data_label) button_frame = tk.Frame(self) button_frame.pack(fill=tk.X, side=tk.RIGHT, anchor=tk.SE) self.start_button = tk.Button(button_frame) self.start_button["text"] = "Start" self.start_button["command"] = self.start self.start_button.grid(row=0, column=0, padx=3, pady=3) quit_button = tk.Button(button_frame) quit_button["text"] = "Quit" quit_button["command"] = self.master.destroy quit_button.grid(row=0, column=1, padx=3, pady=3)
class ULAI07(UIExample): def __init__(self, master=None): super(ULAI07, self).__init__(master) # By default, the example detects all available devices and selects the # first device listed. # If use_device_detection is set to False, the board_num property needs # to match the desired board number configured with Instacal. use_device_detection = True self.board_num = 0 try: if use_device_detection: self.configure_first_detected_device() self.device_info = DaqDeviceInfo(self.board_num) self.ai_info = self.device_info.get_ai_info() if self.ai_info.is_supported and self.ai_info.resolution <= 16: self.create_widgets() else: self.create_unsupported_widgets() except ULError: self.create_unsupported_widgets(True) def update_value(self): channel = self.get_channel_num() ai_range = self.ai_info.supported_ranges[0] try: gain = self.ai_info.supported_ranges[0] trig_type = self.get_trigger_type() trig_value_eng = self.get_trigger_level() trig_value = ul.from_eng_units(self.board_num, gain, trig_value_eng) # Get a value from the device value = ul.a_trig(self.board_num, channel, trig_type, trig_value, gain) # Convert the raw value to engineering units eng_units_value = ul.to_eng_units(self.board_num, ai_range, value) # Display the raw value self.value_label["text"] = str(value) # Display the engineering value self.eng_value_label["text"] = '{:.3f}'.format(eng_units_value) except ULError as e: self.stop() show_ul_error(e) def start(self): self.update_value() def get_channel_num(self): try: return int(self.channel_entry.get()) except ValueError: return 0 def get_trigger_type(self): if self.trigger_type_combobox.get() == "Above": return TrigType.TRIG_ABOVE else: return TrigType.TRIG_BELOW def get_trigger_level(self): try: return float(self.trigger_level_entry.get()) except ValueError: return 0 def validate_channel_entry(self, p): if p == '': return True try: value = int(p) if value < 0 or value > self.ai_info.num_chans - 1: return False except ValueError: return False return True def create_widgets(self): '''Create the tkinter UI''' self.device_label = tk.Label(self) self.device_label.pack(fill=tk.NONE, anchor=tk.NW) self.device_label["text"] = ('Board Number ' + str(self.board_num) + ": " + self.device_info.product_name + " (" + self.device_info.unique_id + ")") main_frame = tk.Frame(self) main_frame.pack(fill=tk.X, anchor=tk.NW) channel_vcmd = self.register(self.validate_channel_entry) float_vcmd = self.register(validate_float_entry) curr_row = 0 channel_entry_label = tk.Label(main_frame) channel_entry_label["text"] = "Channel Number:" channel_entry_label.grid(row=curr_row, column=0, sticky=tk.W) self.channel_entry = tk.Spinbox(main_frame, from_=0, to=max(self.ai_info.num_chans - 1, 0), validate='key', validatecommand=(channel_vcmd, '%P')) self.channel_entry.grid(row=curr_row, column=1, sticky=tk.W) curr_row += 1 trigger_type_label = tk.Label(main_frame) trigger_type_label["text"] = "Trigger Type:" trigger_type_label.grid(row=curr_row, column=0, sticky=tk.W) self.trigger_type_combobox = Combobox(main_frame) self.trigger_type_combobox["values"] = ["Above", "Below"] self.trigger_type_combobox["state"] = "readonly" self.trigger_type_combobox.current(0) self.trigger_type_combobox.grid(row=curr_row, column=1, sticky=tk.W) curr_row += 1 trigger_level_label = tk.Label(main_frame) trigger_level_label["text"] = "Trigger Level (V):" trigger_level_label.grid(row=curr_row, column=0, sticky=tk.W) self.trigger_level_entry = tk.Entry(main_frame, validate='key', validatecommand=(float_vcmd, '%P')) self.trigger_level_entry.grid(row=curr_row, column=1, sticky=tk.W) self.trigger_level_entry.insert(0, "2") curr_row += 1 raw_value_left_label = tk.Label(main_frame) raw_value_left_label["text"] = ("Value read from selected channel:") raw_value_left_label.grid(row=curr_row, column=0, sticky=tk.W) self.value_label = tk.Label(main_frame) self.value_label.grid(row=curr_row, column=1, sticky=tk.W) curr_row += 1 eng_value_left_label = tk.Label(main_frame) eng_value_left_label["text"] = "Value converted to voltage:" eng_value_left_label.grid(row=curr_row, column=0, sticky=tk.W) self.eng_value_label = tk.Label(main_frame) self.eng_value_label.grid(row=curr_row, column=1, sticky=tk.W) curr_row += 1 warning_label = tk.Label(main_frame, justify=tk.LEFT, wraplength=400, fg="red") warning_label["text"] = ( "Warning: Clicking Start will freeze the UI until the " "trigger condition is met. Real-world applications should " "run the a_trig method on a separate thread.") warning_label.grid(row=curr_row, columnspan=2, sticky=tk.W) button_frame = tk.Frame(self) button_frame.pack(fill=tk.X, side=tk.RIGHT, anchor=tk.SE) self.start_button = tk.Button(button_frame) self.start_button["text"] = "Start" self.start_button["command"] = self.start self.start_button.grid(row=0, column=0, padx=3, pady=3) quit_button = tk.Button(button_frame) quit_button["text"] = "Quit" quit_button["command"] = self.master.destroy quit_button.grid(row=0, column=1, padx=3, pady=3)
class ULGT03(UIExample): def __init__(self, master): super(ULGT03, self).__init__(master) self.board_num = 0 self.max_board_num = ul.get_config(InfoType.GLOBALINFO, 0, 0, GlobalInfo.NUMBOARDS) self.create_widgets() self.update_board_info() def update_board_info(self): info_text = "" try: # Raises exception is board_num is not valid self.device_info = DaqDeviceInfo(self.board_num) info_text += self.get_ad_info() info_text += self.get_temperature_info() info_text += self.get_da_info() info_text += self.get_digital_info() info_text += self.get_counter_info() info_text += self.get_expansion_info() # Remove the last "newline" character info_text = info_text[:-1] except ULError: info_text = ( "No board found at board number " + str(self.board_num) + ".\nRun InstaCal to add or remove boards before running this " + "program.") finally: self.info_label["text"] = info_text def get_ad_info(self): result = '' if self.device_info.supports_analog_input: ai_info = self.device_info.get_ai_info() result = ("Number of A/D channels: " + str(ai_info.num_chans) + "\n") return result def get_temperature_info(self): result = '' if self.device_info.supports_temp_input: ai_info = self.device_info.get_ai_info() result = ("Number of Temperature channels: " + str(ai_info.num_temp_chans) + "\n") return result def get_da_info(self): result = '' if self.device_info.supports_analog_output: ao_info = self.device_info.get_ao_info() result = ("Number of D/A channels: " + str(ao_info.num_chans) + "\n") return result def get_digital_info(self): result = '' if self.device_info.supports_digital_io: dio_info = self.device_info.get_dio_info() for port_num in range(len(dio_info.port_info)): result += ("Digital Port #" + str(port_num) + ": " + str(dio_info.port_info[port_num].num_bits) + " bits\n") return result def get_counter_info(self): result = '' if self.device_info.supports_counters: ctr_info = self.device_info.get_ctr_info() result = ("Number of counter devices: " + str(ctr_info.num_chans) + "\n") return result def get_expansion_info(self): result = '' if self.device_info.num_expansions > 0: for exp_info in self.device_info.exp_info: result += ("A/D channel " + str(exp_info.mux_ad_chan) + " connected to EXP (device ID=" + str(exp_info.board_type) + ").\n") return result def board_num_changed(self, *args): try: self.board_num = int(self.board_num_variable.get()) self.update_board_info() except ValueError: self.board_num = 0 def create_widgets(self): '''Create the tkinter UI''' main_frame = tk.Frame(self) main_frame.pack(fill=tk.X, anchor=tk.NW) positive_int_vcmd = self.register(validate_positive_int_entry) board_num_label = tk.Label(main_frame) board_num_label["text"] = "Board Number:" board_num_label.grid(row=0, column=0, sticky=tk.W) self.board_num_variable = StringVar() board_num_entry = tk.Spinbox(main_frame, from_=0, to=self.max_board_num, textvariable=self.board_num_variable, validate="key", validatecommand=(positive_int_vcmd, "%P")) board_num_entry.grid(row=0, column=1, sticky=tk.W) self.board_num_variable.trace("w", self.board_num_changed) info_groupbox = tk.LabelFrame(self, text="Board Information") info_groupbox.pack(fill=tk.X, anchor=tk.NW, padx=3, pady=3) self.info_label = tk.Label(info_groupbox, justify=tk.LEFT, wraplength=400) self.info_label.grid() button_frame = tk.Frame(self) button_frame.pack(fill=tk.X, side=tk.RIGHT, anchor=tk.SE) quit_button = tk.Button(button_frame) quit_button["text"] = "Quit" quit_button["command"] = self.master.destroy quit_button.grid(row=0, column=0, padx=3, pady=3)
class ULAI14(UIExample): def __init__(self, master=None): super(ULAI14, self).__init__(master) # By default, the example detects all available devices and selects the # first device listed. # If use_device_detection is set to False, the board_num property needs # to match the desired board number configured with Instacal. use_device_detection = True self.board_num = 0 try: if use_device_detection: self.configure_first_detected_device() self.device_info = DaqDeviceInfo(self.board_num) self.ai_info = self.device_info.get_ai_info() if self.ai_info.is_supported and self.ai_info.supports_analog_trig: self.create_widgets() else: self.create_unsupported_widgets() except ULError: self.create_unsupported_widgets(True) def start_scan(self): low_chan = self.get_low_channel_num() high_chan = self.get_high_channel_num() if low_chan > high_chan: messagebox.showerror( "Error", "Low Channel Number must be greater than or equal to " "High Channel Number") self.start_button["state"] = tk.NORMAL return rate = 1000 points_per_channel = 10 num_channels = high_chan - low_chan + 1 total_count = points_per_channel * num_channels ai_range = self.ai_info.supported_ranges[0] trig_type = TrigType.TRIG_ABOVE low_threshold_volts = 0.1 high_threshold_volts = 1.53 # Allocate a buffer for the scan if self.ai_info.resolution <= 16: # Use the win_buf_alloc method for devices with a resolution <= 16 memhandle = ul.win_buf_alloc(total_count) else: # Use the win_buf_alloc_32 method for devices with a resolution > # 16 memhandle = ul.win_buf_alloc_32(total_count) # Check if the buffer was successfully allocated if not memhandle: messagebox.showerror("Error", "Failed to allocate memory") self.start_button["state"] = tk.NORMAL return try: low_threshold, high_threshold = self.get_threshold_counts( ai_range, low_threshold_volts, high_threshold_volts) ul.set_trigger(self.board_num, trig_type, low_threshold, high_threshold) # Run the scan ul.a_in_scan(self.board_num, low_chan, high_chan, total_count, rate, ai_range, memhandle, ScanOptions.EXTTRIGGER) # Convert the memhandle to a ctypes array # Note: the ctypes array will only be valid until win_buf_free # is called. # A copy of the buffer can be created using win_buf_to_array # or win_buf_to_array_32 before the memory is freed. The copy can # be used at any time. if self.ai_info.resolution <= 16: # Use the memhandle_as_ctypes_array method for devices with a # resolution <= 16 array = cast(memhandle, POINTER(c_ushort)) else: # Use the memhandle_as_ctypes_array_32 method for devices with # a resolution > 16 array = cast(memhandle, POINTER(c_ulong)) # Display the values self.display_values(array, ai_range, total_count, low_chan, high_chan) except ULError as e: show_ul_error(e) finally: # Free the allocated memory ul.win_buf_free(memhandle) self.start_button["state"] = tk.NORMAL def get_threshold_counts(self, ai_range, low_threshold_volts, high_threshold_volts): if self.ai_info.analog_trig_resolution == 0: # If the trigger resolution from AnalogInputProps is 0, # the resolution of the trigger is the same as the # analog input resolution, and we can use from_eng_units # to convert from engineering units to count low_threshold = ul.from_eng_units(self.board_num, ai_range, low_threshold_volts) high_threshold = ul.from_eng_units(self.board_num, ai_range, high_threshold_volts) else: # Otherwise, the resolution of the triggers are different # from the analog input, and we must convert from engineering # units to count manually trig_range = self.ai_info.analog_trig_range if trig_range == ULRange.UNKNOWN: # If the analog_trig_range is UNKNOWN, the trigger voltage # range is the same as the analog input. trig_range = ai_range low_threshold = self.volts_to_count( low_threshold_volts, self.ai_info.analog_trig_resolution, trig_range) high_threshold = self.volts_to_count( high_threshold_volts, self.ai_info.analog_trig_resolution, trig_range) return low_threshold, high_threshold def volts_to_count(self, volts, resolution, voltage_range): full_scale_count = 2 ** resolution range_min = voltage_range.range_min range_max = voltage_range.range_max return (full_scale_count / (range_max - range_min) * (volts - range_min)) def display_values(self, array, range_, total_count, low_chan, high_chan): new_data_frame = tk.Frame(self.results_group) channel_text = [] # Add the headers for chan_num in range(low_chan, high_chan + 1): channel_text.append("Channel " + str(chan_num) + "\n") chan_count = high_chan - low_chan + 1 # Add (up to) the first 10 values for each channel to the text chan_num = low_chan for data_index in range(0, min(chan_count * 10, total_count)): if self.ai_info.resolution <= 16: eng_value = ul.to_eng_units( self.board_num, range_, array[data_index]) else: eng_value = ul.to_eng_units_32( self.board_num, range_, array[data_index]) channel_text[chan_num - low_chan] += '{:.3f}'.format(eng_value) + "\n" if chan_num == high_chan: chan_num = low_chan else: chan_num += 1 # Add the labels for each channel for chan_num in range(low_chan, high_chan + 1): chan_label = tk.Label(new_data_frame, justify=tk.LEFT, padx=3) chan_label["text"] = channel_text[chan_num - low_chan] chan_label.grid(row=0, column=chan_num - low_chan) self.data_frame.destroy() self.data_frame = new_data_frame self.data_frame.grid() def start(self): self.start_button["state"] = tk.DISABLED self.start_scan() def get_low_channel_num(self): if self.ai_info.num_chans == 1: return 0 try: return int(self.low_channel_entry.get()) except ValueError: return 0 def get_high_channel_num(self): if self.ai_info.num_chans == 1: return 0 try: return int(self.high_channel_entry.get()) except ValueError: return 0 def validate_channel_entry(self, p): if p == '': return True try: value = int(p) if value < 0 or value > self.ai_info.num_chans - 1: return False except ValueError: return False return True def create_widgets(self): '''Create the tkinter UI''' self.device_label = tk.Label(self) self.device_label.pack(fill=tk.NONE, anchor=tk.NW) self.device_label["text"] = ('Board Number ' + str(self.board_num) + ": " + self.device_info.product_name + " (" + self.device_info.unique_id + ")") main_frame = tk.Frame(self) main_frame.pack(fill=tk.X, anchor=tk.NW) curr_row = 0 if self.ai_info.num_chans > 1: channel_vcmd = self.register(self.validate_channel_entry) low_channel_entry_label = tk.Label(main_frame) low_channel_entry_label["text"] = "Low Channel Number:" low_channel_entry_label.grid( row=curr_row, column=0, sticky=tk.W) self.low_channel_entry = tk.Spinbox( main_frame, from_=0, to=max(self.ai_info.num_chans - 1, 0), validate='key', validatecommand=(channel_vcmd, '%P')) self.low_channel_entry.grid( row=curr_row, column=1, sticky=tk.W) curr_row += 1 high_channel_entry_label = tk.Label(main_frame) high_channel_entry_label["text"] = "High Channel Number:" high_channel_entry_label.grid( row=curr_row, column=0, sticky=tk.W) self.high_channel_entry = tk.Spinbox( main_frame, from_=0, validate='key', to=max(self.ai_info.num_chans - 1, 0), validatecommand=(channel_vcmd, '%P')) self.high_channel_entry.grid( row=curr_row, column=1, sticky=tk.W) initial_value = min(self.ai_info.num_chans - 1, 3) self.high_channel_entry.delete(0, tk.END) self.high_channel_entry.insert(0, str(initial_value)) curr_row += 1 self.results_group = tk.LabelFrame( self, text="Results", padx=3, pady=3) self.results_group.pack(fill=tk.X, anchor=tk.NW, padx=3, pady=3) self.data_frame = tk.Frame(self.results_group) self.data_frame.grid() curr_row += 1 warning_label = tk.Label( self, justify=tk.LEFT, wraplength=400, fg="red") warning_label["text"] = ( "Warning: Clicking Start will freeze the UI until the " "trigger condition is met. Real-world applications should " "run the a_in_scan method on a separate thread or use the " "BACKGROUND option.") warning_label.pack(fill=tk.X, anchor=tk.NW, padx=3, pady=3) button_frame = tk.Frame(self) button_frame.pack(fill=tk.X, side=tk.RIGHT, anchor=tk.SE) self.start_button = tk.Button(button_frame) self.start_button["text"] = "Start" self.start_button["command"] = self.start self.start_button.grid(row=0, column=0, padx=3, pady=3) quit_button = tk.Button(button_frame) quit_button["text"] = "Quit" quit_button["command"] = self.master.destroy quit_button.grid(row=0, column=1, padx=3, pady=3)
class ULAI10(UIExample): def __init__(self, master=None): super(ULAI10, self).__init__(master) # By default, the example detects all available devices and selects the # first device listed. # If use_device_detection is set to False, the board_num property needs # to match the desired board number configured with Instacal. use_device_detection = True self.board_num = 0 self.num_elements = 4 self.queue_loaded = False try: if use_device_detection: self.configure_first_detected_device() self.device_info = DaqDeviceInfo(self.board_num) self.ai_info = self.device_info.get_ai_info() if self.ai_info.is_supported: self.create_widgets() self.scan_loop() else: self.create_unsupported_widgets() except ULError: self.create_unsupported_widgets(True) def scan_loop(self): rate = 100 points_per_channel = 10 low_chan = 0 # Ignored by a_in_scan when queue is enabled high_chan = 3 # Ignored by a_in_scan when queue is enabled num_channels = high_chan - low_chan + 1 total_count = points_per_channel * num_channels # Ignored by a_in_scan when queue is enabled range_ = self.ai_info.supported_ranges[0] # Allocate a buffer for the scan if self.ai_info.resolution <= 16: # Use the win_buf_alloc method for devices with a resolution <= 16 memhandle = ul.win_buf_alloc(total_count) else: # Use the win_buf_alloc_32 method for devices with a resolution > # 16 memhandle = ul.win_buf_alloc_32(total_count) # Check if the buffer was successfully allocated if not memhandle: messagebox.showerror("Error", "Failed to allocate memory") return try: # Run the scan ul.a_in_scan(self.board_num, low_chan, high_chan, total_count, rate, range_, memhandle, 0) # Convert the memhandle to a ctypes array # Note: the ctypes array will only be valid until win_buf_free # is called. # A copy of the buffer can be created using win_buf_to_array # or win_buf_to_array_32 before the memory is freed. The copy can # be used at any time. if self.ai_info.resolution <= 16: # Use the memhandle_as_ctypes_array method for devices with a # resolution <= 16 array = cast(memhandle, POINTER(c_ushort)) else: # Use the memhandle_as_ctypes_array_32 method for devices with # a resolution > 16 array = cast(memhandle, POINTER(c_ulong)) # Display the values self.display_values(array, total_count) self.after(1000, self.scan_loop) except ULError as e: show_ul_error(e) finally: # Free the allocated memory ul.win_buf_free(memhandle) def display_values(self, array, total_count): data_text = [] for _ in range(0, self.num_elements): data_text.append("") # Add (up to) the first 10 values for each channel to the text chan_num = 0 for data_index in range(0, min(self.num_elements * 10, total_count)): data_text[chan_num] += str(array[data_index]) + "\n" if chan_num == self.num_elements - 1: chan_num = 0 else: chan_num += 1 # Add the labels for each channel for chan_num in range(0, self.num_elements): self.data_labels[chan_num]["text"] = data_text[chan_num] def toggle_load_queue(self): if not self.queue_loaded: chan_array = [] if self.random_channels_checkbutton_var.get(): for chan_num in range(0, self.num_elements): chan_array.append( random.randint(0, self.ai_info.num_chans - 1)) else: for chan_num in range(0, self.num_elements): chan_array.append(chan_num) gain_array = [] if self.random_ranges_checkbutton_var.get(): for chan_num in range(0, self.num_elements): gain_array.append( random.choice(self.ai_info.supported_ranges)) else: range_ = self.ai_info.supported_ranges[0] for chan_num in range(0, self.num_elements): gain_array.append(range_) try: ul.a_load_queue(self.board_num, chan_array, gain_array, self.num_elements) self.queue_loaded = True self.toggle_load_queue_button["text"] = "Unload Queue" self.instructions_label["text"] = ("Queue loaded on board " + str(self.board_num)) # Update the headers for chan_num in range(0, self.num_elements): self.channel_labels[chan_num]["text"] = ( "Channel " + str(chan_array[chan_num])) for chan_num in range(0, self.num_elements): self.range_labels[chan_num]["text"] = \ gain_array[chan_num].name except ULError as e: if e.errorcode == ErrorCode.BADADCHAN: self.instructions_label["text"] = ( "Board " + str(self.board_num) + " doesn't support random channels. Queue was not " "changed.") else: raise else: # Set Count to 0 to disable the queue ul.a_load_queue(self.board_num, [], [], 0) self.queue_loaded = False self.toggle_load_queue_button["text"] = "Load Queue" self.instructions_label["text"] = ( "Board " + str(self.board_num) + " scanning contiguous channels with with Range set to " + str(self.ai_info.supported_ranges[0])) # Update the headers for chan_num in range(0, self.num_elements): self.channel_labels[chan_num]["text"] = ("Channel " + str(chan_num)) for chan_num in range(0, self.num_elements): self.range_labels[chan_num]["text"] = ( self.ai_info.supported_ranges[0].name) def create_widgets(self): '''Create the tkinter UI''' self.device_label = tk.Label(self) self.device_label.pack(fill=tk.NONE, anchor=tk.NW) self.device_label["text"] = ('Board Number ' + str(self.board_num) + ": " + self.device_info.product_name + " (" + self.device_info.unique_id + ")") main_frame = tk.Frame(self) main_frame.pack(fill=tk.X, anchor=tk.NW) self.instructions_label = tk.Label(main_frame, justify=tk.LEFT, wraplength=400) self.instructions_label["text"] = ( "Board " + str(self.board_num) + " collecting analog data on up to " + str(self.num_elements) + " channels between channel 0 and channel " + str(self.ai_info.num_chans) + " using AInScan and ALoadQueue.") self.instructions_label.pack(fill=tk.X, anchor=tk.NW, padx=3, pady=3) self.results_group = tk.LabelFrame(main_frame, text="Results", padx=3, pady=3) self.results_group.pack(fill=tk.X, anchor=tk.NW, padx=3, pady=3) self.data_frame = tk.Frame(self.results_group) self.data_frame.grid() self.channel_labels = [] self.range_labels = [] self.data_labels = [] # Add the labels for each channel for chan_num in range(0, self.num_elements): chan_label = tk.Label(self.data_frame, justify=tk.LEFT, padx=3) chan_label.grid(row=0, column=chan_num) chan_label["text"] = "Channel " + str(chan_num) self.channel_labels.append(chan_label) range_label = tk.Label(self.data_frame, justify=tk.LEFT, padx=3) range_label.grid(row=1, column=chan_num) range_label["text"] = self.ai_info.supported_ranges[0].name self.range_labels.append(range_label) data_label = tk.Label(self.data_frame, justify=tk.LEFT, padx=3) data_label.grid(row=2, column=chan_num) self.data_labels.append(data_label) self.random_ranges_checkbutton_var = IntVar(value=1) random_ranges_checkbutton = tk.Checkbutton( main_frame, text="Random Ranges", variable=self.random_ranges_checkbutton_var, borderwidth=0) random_ranges_checkbutton.pack(fill=tk.X, anchor=tk.NW, padx=3, pady=3) self.random_channels_checkbutton_var = IntVar(value=0) random_channels_checkbutton = tk.Checkbutton( main_frame, text="Random Channels", variable=self.random_channels_checkbutton_var, borderwidth=0) random_channels_checkbutton.pack(fill=tk.X, anchor=tk.NW, padx=3, pady=3) button_frame = tk.Frame(self) button_frame.pack(fill=tk.X, side=tk.RIGHT, anchor=tk.SE) self.toggle_load_queue_button = tk.Button(button_frame) self.toggle_load_queue_button["text"] = "Load Queue" self.toggle_load_queue_button["command"] = \ self.toggle_load_queue self.toggle_load_queue_button.grid(row=0, column=1, rowspan=2, padx=3, pady=3) quit_button = tk.Button(button_frame) quit_button["text"] = "Quit" quit_button["command"] = self.master.destroy quit_button.grid(row=0, column=2, rowspan=2, padx=3, pady=3)