Jan-09-2025, 12:19 PM
Hello there.
I've been searching kinda every place I know of and I am completely exhausted.
Thus, I want to ask other developers for their expertise.
I have an ADDI-DATA APCI-1516 which I want to talk to using python. Specifically, I want to toggle outputs based on variable frequencies.
I've developed a user interface using tkinter and everything works fine so far.
I instantiated the driver functions using ctypes.
However, upon compiling the script using auto-py-to-exe and running the executable, everything works fine up until I want to toggle the outputs.
The program opens the card's communication, but I don't see any LEDs flickering on my PX901-DG breakout board.
And yes, a 24V power supply is connected to it, with the jumpers correctly positioned.
The drivers are installed, the device manager sees the card and the program is able to talk to it.
Btw, I am running Win10 64bit, Python 3.11.4
Now here is my code (idk if it will display as code or as text):
I've been searching kinda every place I know of and I am completely exhausted.
Thus, I want to ask other developers for their expertise.
I have an ADDI-DATA APCI-1516 which I want to talk to using python. Specifically, I want to toggle outputs based on variable frequencies.
I've developed a user interface using tkinter and everything works fine so far.
I instantiated the driver functions using ctypes.
However, upon compiling the script using auto-py-to-exe and running the executable, everything works fine up until I want to toggle the outputs.
The program opens the card's communication, but I don't see any LEDs flickering on my PX901-DG breakout board.
And yes, a 24V power supply is connected to it, with the jumpers correctly positioned.
The drivers are installed, the device manager sees the card and the program is able to talk to it.
Btw, I am running Win10 64bit, Python 3.11.4
Now here is my code (idk if it will display as code or as text):
import ctypes.wintypes
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
from PIL import Image, ImageTk
from math import floor
import time
import threading
import ctypes
import sys
import os
class GlobalVariables:
pass
class PCICommunication:
def __init__(self):
self.returnValue = -1
self.dll = ctypes.CDLL('C:/Windows/System32/PCI1516.dll')
#Acquiring no of boards
self.numberOfBoards = ctypes.wintypes.BYTE()
self.dll.i_PCI1516_GetNumberOfBoards.argtypes = [ctypes.POINTER(ctypes.wintypes.BYTE)]
self.dll.i_PCI1516_GetNumberOfBoards.restype = ctypes.c_int
#Acquiring board data
self.boardIdentifierSize = 256
self.boardIdentifier = ctypes.create_string_buffer(self.boardIdentifierSize)
self.uiNumber = ctypes.wintypes.DWORD()
self.deviceNumber = ctypes.wintypes.DWORD()
self.busNumber = ctypes.wintypes.DWORD()
self.baseAddress0 = ctypes.wintypes.DWORD()
self.baseAddress1 = ctypes.wintypes.DWORD()
self.interrupt = ctypes.wintypes.BYTE()
self.dll.i_PCI1516_GetBoardInformation.argtypes = [
ctypes.wintypes.BYTE, # boardIndex
ctypes.c_char_p, # boardIdentifier
ctypes.wintypes.DWORD, # boardIdentifierSize
ctypes.POINTER(ctypes.wintypes.DWORD), # uiNumber
ctypes.POINTER(ctypes.wintypes.DWORD), # deviceNumber
ctypes.POINTER(ctypes.wintypes.DWORD), # busNumber
ctypes.POINTER(ctypes.wintypes.DWORD), # baseAddress0
ctypes.POINTER(ctypes.wintypes.DWORD), # baseAddress1
ctypes.POINTER(ctypes.wintypes.BYTE)] # interrupt
self.dll.i_PCI1516_GetBoardInformation.restype = ctypes.c_int
#Open communication
self.deviceHandle = ctypes.wintypes.HANDLE()
self.dll.i_PCI1516_OpenBoardViaIdentifier.argtypes = [
ctypes.c_char_p,
ctypes.POINTER(ctypes.wintypes.HANDLE)]
self.dll.i_PCI1516_OpenBoardViaIdentifier.restype = ctypes.c_int
#Close communication
self.dll.i_PCI1516_CloseBoard.argtypes = (ctypes.wintypes.HANDLE,)
self.dll.i_PCI1516_CloseBoard.restype = ctypes.c_int
#Activate digital memory - necessary to write to channels
self.dll.i_PCI1516_SetDigitalOutputMemoryOn.argtypes = (ctypes.wintypes.HANDLE,)
self.dll.i_PCI1516_SetDigitalOutputMemoryOn.restype = ctypes.c_int
#Set output channel
self.dll.i_PCI1516_Set1DigitalOutputOn.argtypes = [
ctypes.wintypes.HANDLE,
ctypes.wintypes.BYTE]
self.dll.i_PCI1516_Set1DigitalOutputOn.restype = ctypes.c_int
#Reset output channel
self.dll.i_PCI1516_Set1DigitalOutputOff.argtypes = [
ctypes.wintypes.HANDLE,
ctypes.wintypes.BYTE]
self.dll.i_PCI1516_Set1DigitalOutputOff.restype = ctypes.c_int
def OpenCommunication(self):
self.returnValue = self.dll.i_PCI1516_GetNumberOfBoards(ctypes.byref(self.numberOfBoards))
if self.numberOfBoards.value == 0:
noDevicesFoundWindow = messagebox.showerror('No device found', 'No xPCI1516 device found')
if noDevicesFoundWindow:
quit()
#Right now this method is only capable of communicating with ONE card, indexed at 0
if self.numberOfBoards.value > 0:
for i in range(self.numberOfBoards.value):
self.returnValue = self.dll.i_PCI1516_GetBoardInformation(i,
self.boardIdentifier,
self.boardIdentifierSize,
ctypes.byref(self.uiNumber),
ctypes.byref(self.deviceNumber),
ctypes.byref(self.busNumber),
ctypes.byref(self.baseAddress0),
ctypes.byref(self.baseAddress1),
ctypes.byref(self.interrupt))
print(f'Index: {i}')
print(f'Identifier: {self.boardIdentifier.value}')
print(f'Device no: {self.deviceNumber.value}')
print(f'Bus no: {self.busNumber.value}')
print(f'Base addr 0: {self.baseAddress0.value}')
print(f'Base addr 1: {self.baseAddress1.value}')
deviceToOpen = input('Which board should be opened (input identifier)?')
self.returnValue = self.dll.i_PCI1516_OpenBoardViaIdentifier(ctypes.c_char_p(deviceToOpen.encode('utf-8')),
ctypes.byref(self.deviceHandle))
print(f'Opening: {self.returnValue}')
if self.returnValue != 0:
quittingWindow = messagebox.askretrycancel('Failed setup', f'Could not establish communication with PCI board.\n\n Error code: {self.returnValue}')
if quittingWindow:
self.OpenCommunication()
else:
quit()
self.returnValue = self.dll.i_PCI1516_SetDigitalOutputMemoryOn(self.deviceHandle)
if self.returnValue != 0:
quittingWindow = messagebox.askretrycancel('Failed setup', f'Could not activate digital memory.\n\n Error code: {self.returnValue}')
if quittingWindow:
self.OpenCommunication()
else:
quit()
def CloseCommunication(self):
'''Close PCI communication'''
self.returnValue = self.dll.i_PCI1516_CloseBoard(self.deviceHandle)
print(self.returnValue)
def SetOutputChannel(self, channel):
'''Set PCI output channel'''
self.returnValue = self.dll.i_PCI1516_Set1DigitalOutputOn(self.deviceHandle, channel)
print(f'Turning on channel: {channel}. Return: {self.returnValue}')
def ResetOutputChannel(self, channel):
'''Reset PCI output channel'''
self.returnValue = self.dll.i_PCI1516_Set1DigitalOutputOff(self.deviceHandle, channel)
print(f'Turning off channel: {channel}. Return: {self.returnValue}')
class UserInterface:
frames = []
def __init__(self):
self.mainWindow = Tk()
self.mainWindow.title('ADDI-DATA Communication')
self.mainWindow.geometry('372x212')
self.mainWindow.resizable(False, False)
#Create other widgets
self.outputAreaFrame = Frame(self.mainWindow, relief = 'sunken', borderwidth = 1, width = 362, height = 182)
self.bottomBar = Frame(self.mainWindow, relief = 'raised', borderwidth = 1)
self.versionLabel = Label(self.bottomBar, text = 'V0.1')
self.creatorLabel = Label(self.bottomBar, text = 'Developed by Lux A.')
#Pack widgets
self.outputAreaFrame.pack(padx = 5, pady = 5, fill = 'both')
self.outputAreaFrame.pack_propagate(False)
self.outputAreaFrame.grid_propagate(False)
self.bottomBar.pack(fill = 'x')
self.versionLabel.pack(side = 'left')
self.creatorLabel.pack(side = 'right')
for i in range(8):
self.CreateNewOutputArea()
self.mainWindow.lift()
self.mainWindow.mainloop()
def CreateNewOutputArea(self):
'''This function is used to create a new area including a toggle switch and frequency input as well as a close button.
It appends it to the frames array.
The OutputAreas will be arranged in a WxH 8x4 grid'''
index = len(self.frames)
outputArea = self.OutputArea(master = self.outputAreaFrame, index = index)
self.frames.append([index, outputArea])
class OutputArea:
def __init__(self, master, index):
self.state = 'off'
self.index = index
#Import images and create ressources
try:
basePath = sys._MEIPASS #Path where pyInstaller unpacks additional files, used in .exe
except Exception:
basePath = os.path.abspath('.') #Path of current working directory, used when developing
self.offButtonPNG = ImageTk.PhotoImage(Image.open(os.path.join(basePath, 'images/Toggle_Switch_Off.png')).resize((40, 20), Image.LANCZOS))
self.onButtonPNG = ImageTk.PhotoImage(Image.open(os.path.join(basePath, 'images/Toggle_Switch_On.png')).resize((40, 20), Image.LANCZOS))
#Create widgets
self.areaFrame = Frame(master, width = 88, height = 88, relief = 'sunken', borderwidth = 1)
self.indexLabel = Label(self.areaFrame, text = self.index + 1)
self.horizontalSeparator = ttk.Separator(self.areaFrame, orient = 'horizontal')
self.toggleOnOffButton = Button(self.areaFrame, image = self.offButtonPNG, relief = 'flat', command = self.ToggleState)
self.frequencyInput = Spinbox(self.areaFrame, from_ = 0, to = 1000, width = 10, state = 'readonly')
#Pack widgets
self.areaFrame.grid_propagate(False)
self.indexLabel.grid(row = 0, columnspan = 2, sticky = 'NEWS')
self.horizontalSeparator.grid(row = 1, column = 0, columnspan = 2, sticky = 'WE')
self.toggleOnOffButton.grid(row = 2, column = 0, sticky = 'NWS')
self.frequencyInput.grid(row = 3, column = 0, columnspan = 2, sticky = 'NEWS', pady = 12, padx = 5)
#Create area
self.areaFrame.grid(row = floor(self.index / 4), column = self.index % 4, padx = 1, pady = 1)
#Create the thread to output the frequency
self.CreateThread()
def ToggleState(self):
if self.state == 'off':
self.state = 'on'
self.toggleOnOffButton.configure(image = self.onButtonPNG)
self.CreateThread()
else:
self.state = 'off'
self.toggleOnOffButton.configure(image = self.offButtonPNG)
def ToggleOutput(self):
while self.state == 'on':
frequency = int(self.frequencyInput.get())
if frequency > 0:
period = 1 / frequency
#Frequented output
COM.SetOutputChannel(channel = self.index)
time.sleep(period / 2)
COM.ResetOutputChannel(channel = self.index)
time.sleep(period / 2)
else:
#permanent output
COM.SetOutputChannel(channel = self.index)
def CreateThread(self):
thread = threading.Thread(target = self.ToggleOutput)
thread.daemon = True #Daemonized threads exit when the main program exits
thread.start()
if __name__ == '__main__':
COM = PCICommunication()
COM.OpenCommunication()
UI = UserInterface()
