Jan-13-2025, 11:43 AM
(Jan-10-2025, 03:20 PM)deanhystad Wrote: I think your logic is wrong. I would expect that turning an LED ON or OFF would be a single message, but your program sends a continuous stream of messages. I think the only time you need to create a thread is when state == on and frequency > 0, otherwise ToggleOutput should just send a Set or Reset output channel command. If you do create a thread to periodically blink the output, the thread should terminate when the blinking stops (frequency set to 0 or state to off).
You could create a thread for each output, but I think it makes more sense to make 1 thread that handles all the outputs. Better yet, use a periodic event instead of a bunch of threads. You really don't want any part of your program to sleep.
I would get rid of all the threads and create a timer "event" using the .after() command. This code creates a list of blinking lamps that is updated every millisecond. The lamps use a counter to decide when it is time to toggle the lamp. Since I don't have hardware I simulate the lamps with a label that blinks on and off.
import tkinter as tk from tkinter import ttk class Lamp(tk.Frame): """Controls for a lamp.""" def __init__(self, parent, index, command=None): """Initialize lamp. Arguments: parent: My parent widget index: index of lamp I control. command: Command to execute when state or frequency changes. """ super().__init__(parent, borderwidth=1) self.state = 'off' self.index = index self.value = 0 self.frequency = 0 self._command = command self._freq_count = 0 self._counter = 0 label = tk.Label(self, text=f"Lamp {index}") self.toggle_button = tk.Button( self, text="Off", width=3, command=self._toggle_state ) self.display = tk.Label(self, text=" ", width=1) self.freq_input = tk.StringVar(self, "0") spinbox = ttk.Spinbox( self, textvariable=self.freq_input, from_=0, to=500, command=self._frequency_changed ) # Callback for entering text in the spinbox. spinbox.bind('<Return>', self._frequency_changed) spinbox.bind('<FocusOut>', self._frequency_changed) label.grid(row=0, column=0, sticky='news') self.display.grid(row=0, column=1, sticky="news") self.toggle_button.grid(row=1, column=0, columnspan=2, sticky='news') spinbox.grid(row=2, column=0, columnspan=2, sticky='news') def blinking(self): """Return True if this lamp is blinking.""" return self.state == "on" and self.frequency > 0 def blink(self): """Update the lamp.""" self._counter = self._counter - 1 if self._counter <= 0: self._set_value(int(not self.value)) self._counter = self._freq_count def __str__(self): return f"Lamp {self.index}, state={self.state}, frequency={self.frequency}" def __repr__(self): return f"<{self}>" def _set_value(self, value): """Turn lamp on/off.""" self.value = value self.display["text"] = "0" if value else " " # Replace with call to Set or Reset output channel. def _toggle_state(self): """Toggle the lamp state between on and off.""" if self.state == 'off': self.state = 'on' self.toggle_button.configure(text="On") self._counter = self.frequency self._set_value(1) else: self.state = 'off' self.toggle_button.configure(text="Off") self._set_value(0) if self._command is not None: self._command(self) def _frequency_changed(self, *_): """Called when new value entered for frequency.""" try: self.frequency = max(0, min(500, int(self.freq_input.get()))) except (ValueError): self.frequency = 0 self.freq_input.set(self.frequency) self._freq_count = 0 if self.frequency == 0 else 1000 // self.frequency self._counter = self._freq_count if self.frequency == 0: self._set_value(int(self.state == "on")) if self._command is not None: self._command(self) class UserInterface(tk.Tk): """Gui for controlling lamps.""" def __init__(self): super().__init__() self._lamps = [] for i in range(8): lamp = Lamp(self, i, command=self._lamp_changed) lamp.grid(row=i // 4, column=i % 4, sticky='news') self._lamps.append(lamp) self._blinking_lamps = [] def _lamp_changed(self, lamp): """Called when lamp state or frequency changed.""" print(lamp) if lamp.blinking(): if lamp not in self._blinking_lamps: self._blinking_lamps.append(lamp) if len(self._blinking_lamps) == 1: self._blink() elif lamp in self._blinking_lamps: self._blinking_lamps.remove(lamp) print(self._blinking_lamps) def _blink(self): """Update the blinking lamps.""" if self._blinking_lamps: for lamp in self._blinking_lamps: lamp.blink() self.after(1, self._blink) UserInterface().mainloop()
Heyo and thanks for your answer.
I don't think this is a problem. It could be a problem to continuosly set the output if the desired frequency is 0Hz, but it didn't look like it yet.
You might've seen that I even print out each response, the output is always 0, so the card accepts the command and responds with 0 (no error).
Another point that takes my focus off of my code is that even the example executables that come with the driver pack can't get the card to set or reset any lights.
We've tested the driver examples on a different setup (Win 7, 32 bit, APCI1500) and it worked. I do NOT think it has to do with the card as both of my APCI1516s don't work, rather than it being the operating system.
The driver pack I download is the multiarchitecture driver pack for 64 and 32 it systems, but as of now I am at a point where I think it has to do with bit-depth.
Originally the card was intended for operation in 32bit systems, could it be that it simply isn't made for 64bit operation?