Controller_powers/interface.py
2025-01-21 00:00:09 +03:00

486 lines
18 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import base64
import time
from threading import Timer
import tkinter as tk
from tkinter import ttk
import controller.controller_reifined as controller
controller = controller.ControllerInterface(controller.ControllerRefined(0xFFFFFFFFFFFF, 0x112233445542))
# import controller.controller as controller
# controller = controller.ControllerInterface(controller.Controller(0xFFFFFFFFFFFF, 0x112233445542))
win = tk.Tk()
device_choice = ttk.Combobox()
selected_port = tk.StringVar()
dmx_address_field = ttk.Entry()
personality_combobox = ttk.Combobox(win)
failed_to_read_style = ttk.Style(win)
failed_to_read_style.configure('failedToRead.TEntry', foreground="red")
status = tk.StringVar()
status.set('Пожалуйста, выберите COM порт')
saved_status = ''
current_entries = list()
modes = []
connected = False
last_click_time = time.time()
check = tk.IntVar()
def setStatus(new_status: str):
print(f'New status: {new_status}')
status.set(new_status)
win.update()
win.update_idletasks()
def resetStatus() -> None:
global saved_status
setStatus(saved_status)
def setTempStatus(new_status: str, reset_time: float):
global saved_status
if status.get() != new_status:
saved_status = status.get()
setStatus(new_status)
Timer(reset_time, resetStatus).start()
def resetAllStyles() -> None:
for e in current_entries:
e.configure(style='TEntry')
dmx_address_field.configure(style='TEntry')
personality_combobox.configure(style='TCombobox')
def clearAllFields() -> None:
dmx_address_field.delete(0, tk.END)
personality_combobox.set('')
personality_combobox.configure(values=[])
for e in current_entries:
e.delete(0, tk.END)
def comPortSelected(event) -> None:
global connected
connected = False
resetAllStyles()
clearAllFields()
setStatus(f'Подключение к {selected_port.get()}...')
controller.close()
controller.connect(selected_port.get())
print(selected_port.get())
if readControllerCurrentsAndData():
setStatus(f'Выбранный порт: {selected_port.get()}')
connected = True
else:
setStatus(f'Не удалось подключиться к {selected_port.get()}')
device_choice.set('')
connected = False
controller.close()
def refreshComPorts(event) -> None:
device_choice['values'] = controller.readComPorts()
def connectionLost() -> None:
controller.close()
setStatus('Пожалуйста, выберите порт')
setTempStatus(f'Подключение к {controller.controller.ser.port} потеряно!', 2)
resetAllStyles()
device_choice.set('')
clearAllFields()
def readControllerCurrentsAndData() -> bool:
setStatus(f'Чтение токов {controller.controller.ser.port}...')
values = controller.readCurrents()
if not values:
for e in current_entries:
e.configure(style='failedToRead.TEntry')
else:
index = 0
for e in current_entries:
e.configure(style='TEntry')
e.delete(0, tk.END)
e.insert(tk.END, values[index] * 10)
index = index + 1
setStatus(f'Чтение адреса {controller.controller.ser.port}...')
address = controller.readAddress()
print(f'New address: {address}')
if address == -1:
dmx_address_field.configure(style='failedToRead.TEntry')
else:
dmx_address_field.configure(style='TEntry')
dmx_address_field.delete(0, tk.END)
dmx_address_field.insert(tk.END, str(address))
modes = []
setStatus(f'Чтение режимов {controller.controller.ser.port}...')
dev_info = controller.readDeviceInfo()
if dev_info:
mode_count = dev_info[2]
for index in range(mode_count):
setStatus(f'Чтение режима {index + 1} {controller.controller.ser.port}...')
mode = controller.readMode(index)
if not mode:
modes = []
break
modes.append(mode)
time.sleep(0.05)
if not modes:
personality_combobox.set('ERROR')
else:
personality_combobox.configure(style='TCombobox')
personality_combobox.configure(values=modes)
mode_names = []
for index, m in enumerate(modes):
mode_names.append(f'{index + 1}:{m[1]}')
personality_combobox.set(mode_names[dev_info[1] - 1])
personality_combobox.configure(values=mode_names)
return values or address != -1 or modes
def writeCurrentsAddressAndMode() -> None:
toWrite = []
for e in current_entries:
x = e.get()
if x != "":
toWrite.append(int(x) // 10)
else:
toWrite.append(0)
setStatus(f'Запись токов в {controller.controller.ser.port}...')
print(f'Writing... {toWrite}')
if not controller.writeCurrents(toWrite):
connectionLost()
return
setStatus(f'Запись адреса в {controller.controller.ser.port}...')
if dmx_address_field.get() != "" and not controller.writeAddress(int(dmx_address_field.get())):
connectionLost()
return
setStatus(f'Запись режима в {controller.controller.ser.port}...')
if not controller.writeMode(personality_combobox.current() + 1):
connectionLost()
return
if check.get() == 0:
setStatus(f'Выбранный порт: {controller.controller.ser.port}')
setTempStatus(f'Данные были записаны в {controller.controller.ser.port}', 1)
else:
time.sleep(0.5)
dmx_save = dmx_address_field.get()
pers_save = personality_combobox.get()
currents_save = []
for entry in current_entries:
currents_save.append(entry.get())
global modes
setStatus(f'Чтение адреса: {controller.controller.ser.port}...')
dmx_real = controller.readAddress()
setStatus(f'Чтение режима: {controller.controller.ser.port}...')
dev_info = controller.readDeviceInfo()
setStatus(f'Чтение токов: {controller.controller.ser.port}...')
time.sleep(1)
currents_real = controller.readCurrents()
if dmx_real == -1 or not dev_info or not currents_real:
connectionLost()
return
pers_real = f'{dev_info[1]}:{modes[dev_info[1] - 1][1]}'
failed = False
if dmx_save == "" or int(dmx_save) != dmx_real:
failed = True
dmx_address_field.configure(style='failedToRead.TEntry')
else:
dmx_address_field.configure(style='TEntry')
if pers_save != pers_real:
failed = True
personality_combobox.set('ERROR')
else:
personality_combobox.configure(style='TCombobox')
for index in range(4):
if currents_save[index] == "" or int(currents_save[index]) != currents_real[index] * 10:
failed = True
current_entries[index].configure(style='failedToRead.TEntry')
else:
current_entries[index].configure(style='TEntry')
setStatus(f'Выбранный порт: {controller.controller.ser.port}')
if failed:
setTempStatus('Обнаружены ошибки, повторите запись', 1)
else:
setTempStatus('Проверка прошла успешно', 1)
def show_personality_callback() -> None:
popup = tk.Toplevel(win)
popup.grab_set()
popup.resizable(False, False)
popup.title('Информация о режимах')
columns = (
("Номер", 50),
("Название", 100),
("Каналов", 65),
("Плавность", 80),
("Поведение без DMX", 160),
("Описание", 200),
)
for index, c in enumerate(columns):
label = tk.Label(popup, text=f'{c[0]}', width=c[1] // 8, borderwidth=1, relief="solid")
label.grid(row=0, column=index, sticky="nsew")
mode_info = [
(1, "1CH MAX FST", 'нет', 'максимальная яркость'),
(2, "1CH MAX SLO", 'есть', 'максимальная яркость'),
(3, "1CH KEEP FST", 'нет', 'сохранение кадра'),
(4, "1CH KEEP SLO", 'есть', 'сохранение кадра'),
(5, "1CH DARK FST", 'нет', 'гашение'),
(6, "1CH DARK SLO", 'есть', 'гашение'),
(7, "2CH MAX FST", 'нет', 'максимальная яркость'),
(8, "2CH MAX SLO", 'есть', 'максимальная яркость'),
(9, "2CH KEEP FST", 'нет', 'сохранение кадра'),
(10, "2CH KEEP SLO", 'есть', 'сохранение кадра'),
(11, "2CH DARK FST", 'нет', 'гашение'),
(12, "2CH DARK FST", 'есть', 'гашение'),
(13, "3 COLOR CHG", 'нет', 'сохранение кадра'),
(14, "4 COLOR CHG", 'нет', 'сохранение кадра'),
(15, "4CH MAXA FST", 'нет', 'максимальная яркость всех'),
(16, "4CH MAXA SLO", 'есть', 'максимальная яркость всех'),
(17, "4CH MAXW FST", 'нет', 'максимальная яркость белый'),
(18, "4CH MAXW SLO", 'есть', 'максимальная яркость.белый'),
(19, "4CH KEEP FST", 'нет', 'сохранение кадра'),
(20, "4CH KEEP SLO", 'есть', 'сохранение кадра'),
(21, "4CH DARK FST", 'нет', 'гашение'),
(22, "4CH DARK SLO", 'есть', 'гашение'),
(23, "4CH RB3S FST", 'нет', 'RGB перелив синхронизированный'),
(24, "4CH RB3S SLO", 'есть', 'RGB перелив синхронизированный'),
(25, "4CH RB3A FST", 'нет', 'RGB перелив индивидуальный'),
(26, "4CH RB3A SLO", 'есть', 'RGB перелив индивидуальный'),
(27, "4CH RB4S FST", 'нет', 'RGBW перелив синхронизированный'),
(28, "4CH RB4S SLO", 'есть', 'RGBW перелив синхронизированный'),
(29, "4CH RB4A FST", 'нет', ' RGBW перелив индивидуальный '),
(30, "4CH RB4A SLO", 'есть', 'RGBW перелив индивидуальный '),
]
for index in range(len(mode_info)):
for j in range(2):
label = tk.Label(popup, text=f'{mode_info[index][j]}', borderwidth=1, relief="solid")
label.grid(row=index + 1, column=j, sticky="nsew")
for index in range(len(mode_info)):
for j in range(2, 4):
label = tk.Label(popup, text=f'{mode_info[index][j]}', borderwidth=1, relief="solid")
label.grid(row=index + 1, column=j + 1, sticky="nsew")
descr = [
('Режим одноканального диммера. \nВсе выходные каналы работают параллельно', 6),
('Режим двухканального диммера. \nВыходные каналы 1,2 и 3,4 работают параллельно', 6),
(
'Режим RGB перелива с изменением яркости каждого канала. \nW не управляется. Последний канал скорость перелива. ',
1),
(
'Режим RGB перелива с изменением яркости каждого канала. \nW управляется отдельно по DMX. Последний канал скорость перелива',
1),
('Режим четырехканального диммера. \nВсе выходные каналы работают независимо', 16)
]
delta = 0
for index, c in enumerate(descr):
label = tk.Label(popup, text=f'{c[0]}', borderwidth=1, relief="solid")
label.grid(row=delta + 1, column=5, sticky="nsew", rowspan=c[1])
delta += c[1]
chan = [(1, 6), (2, 6), (4, 1), (5, 1), (4, 16)]
delta = 0
for index, c in enumerate(chan):
label = tk.Label(popup, text=f'{c[0]}', borderwidth=1, relief="solid")
label.grid(row=delta + 1, column=2, sticky="nsew", rowspan=c[1])
delta += c[1]
def readCallback() -> None:
global last_click_time
if time.time() - last_click_time < 1:
return
last_click_time = time.time()
if connected:
if not readControllerCurrentsAndData():
connectionLost()
return
setStatus(f'Выбранный порт: {controller.controller.ser.port}')
setTempStatus(f'Токи прочитаны, порт: {controller.controller.ser.port}', 1)
else:
setTempStatus('Порт не выбран!', 1)
def writeCallback() -> None:
global last_click_time
if time.time() - last_click_time < 2:
return
last_click_time = time.time()
if connected:
writeCurrentsAddressAndMode()
else:
setTempStatus('Порт не выбран!', 1)
def validate_dmx_address_entry(entry_data: str) -> bool:
if entry_data.isdigit():
return 1 <= int(entry_data) <= 512
if not entry_data:
return True
return False
def validate_current_entry(entry_data: str) -> bool:
if entry_data.isdigit():
return int(entry_data) <= 2500
if not entry_data:
return True
return False
def finish():
win.destroy()
controller.close()
def power_test() -> None:
controller.connect("/dev/ttyUSB0")
pws = controller.readPowers()
print(pws)
controller.writePowers([30, 47, 60, 60], [14, 45, 34, 9], 20, [10, 20, 30, 40], 10)
time.sleep(2)
pws = controller.readPowers()
print(pws)
if __name__ == "__main__":
win.configure(width=270, height=350)
win.resizable(False, False)
win.title("Controllers")
win.protocol("WM_DELETE_WINDOW", finish)
com_port_label = ttk.Label(win)
com_port_label.configure(font="{Arial} 12 {}", text='COM порт:')
com_port_label.place(anchor="nw", relx=0.05, rely=0.05)
device_choice = ttk.Combobox(win, name="device_choice", textvariable=selected_port)
device_choice.configure(justify="center")
device_choice.place(anchor="nw", relx=0.40, rely=0.04, relwidth=0.4, relheight=0.08)
device_choice['values'] = controller.readComPorts()
device_choice['state'] = 'readonly'
device_choice.bind('<<ComboboxSelected>>', comPortSelected)
device_choice.bind('<Enter>', refreshComPorts)
dmx_address_label = ttk.Label(win)
dmx_address_label.configure(font="{Arial} 12 {}", text='DMX Адрес:')
dmx_address_label.place(anchor="nw", relx=0.05, rely=0.15)
validate_callback = win.register(validate_dmx_address_entry)
dmx_address_field = ttk.Entry(win)
dmx_address_field.configure(font="{Arial} 12 {}", justify="center", validate='key',
validatecommand=(validate_callback, '%P'))
dmx_address_field.place(anchor="nw", relx=0.40, rely=0.15, relwidth=0.2, relheight=0.06)
personality_label = ttk.Label(win)
personality_label.configure(font="{Arial} 12 {}", text='Personality:')
personality_label.place(anchor="nw", relx=0.05, rely=0.26)
image = base64.b64decode(
b'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAnxJREFUOE+lk01IVUEUx/9zZ+Z+Pg2DsIWiSQaBYrgoREqkyFUEQWihqyQR+6KtywrbVERUhkQSCG1q1aJNkYmiqSUUQQuJapFZfvR8+nz3Y07cufLSPlYd7r0wc87/N/9zZ4bhP4P9S3/wzAgFyoCKFAgBXtxu+GvtH5NN5yZpMcsRKQD5LIPgBBVm8bKvboMmPzh0fpgWcx6yOQIzGOqrCrGzzMGqrzD8LoMPX/3ErAow3lu7Dr3Ww96uKYrFnstxrasc1RUuokhBKUIUEXoezODpm2VtS0U+xm/u0hD9iW1/zxgAY/GDnvZScM7wZCKNw3WFqCqz8fmbj/ZbMzofR255Dq9665Phno4pinumeMQYXJvDV0BEDNXlNq6eKMaX+RCtN2JAsgiiABPXqxiL//Zc1knMGAzEGHIhIYgYGGe4cHwL9td4GBjKoH8ok3cAAiavVDLWeGqM0jlLr7ziK2R9giEMCJOjs6kIbY2FePvJR/fDNCK9etIDEWH04jbGGjpHaCl0sbAUIlTQQmEK3fedzmLkAoWT/T+wFDJwYegdSnaD8Ly7hLF9HYP0cd5FEIslh7SS90BNCs31KUzPRrg75kPGYMlh8AQgoPD47NbET+mRMVIG10UaYAuYtkCBJ0BSwHQETEvofOwiDmM1g0enSxPAjqODtMK8NUAirq2wcflYEZ69D3D/tYLlSj2vWzSAgRbv1zmIIRXNo0SmDdPiunB3pYNLLUUYng7RNx7B9kxYnoTjSrD0LO61lWwExJDtrRPEbUsDTFuieLNEzhCQjtQAr0DC9Bfy4vxJXH8jK9uGyE5tguk6MB0JyxVIeQKmEeief7+9PwEI7uIRQxNy9wAAAABJRU5ErkJgggAA')
personality_button = ttk.Button(win, command=show_personality_callback)
question_image = tk.PhotoImage(data=image, format='png')
personality_button.configure(compound="right", default="normal", image=question_image)
personality_button.place(anchor="nw", relx=0.87, rely=0.246, relwidth=0.1)
personality_combobox_font = ('TkDefaultFont', 9)
personality_combobox = ttk.Combobox(win, font=personality_combobox_font)
personality_combobox.configure(
exportselection=True,
justify="left",
state="readonly",
takefocus=False)
personality_combobox.place(anchor="nw", relx=0.40, rely=0.246, relwidth=0.45, relheight=0.085)
for i in range(4):
channel_label = ttk.Label(win)
channel_label.configure(font="{Arial} 12 {}", text=f'Ток канал {i + 1}:')
channel_label.place(anchor="nw", relx=0.05, rely=0.38 + 0.08 * i)
validate_callback = win.register(validate_current_entry)
ent = ttk.Entry(win)
ent.configure(font="{Arial} 12 {}", justify="center", validate='key',
validatecommand=(validate_callback, '%P'))
ent.place(anchor="nw", relx=0.40, rely=0.38 + 0.08 * i, relwidth=0.2, relheight=0.06)
current_entries.append(ent)
ma_label = ttk.Label(win)
ma_label.configure(font="{Arial} 12 {}", text='mA')
ma_label.place(anchor="nw", relx=0.65, rely=0.38 + 0.08 * i)
read_button = ttk.Button(win)
read_button.configure(text='Чтение', command=readCallback)
read_button.place(anchor="nw", relx=0.05, rely=0.7, relwidth=0.37)
write_button = ttk.Button(win)
write_button.configure(text='Запись', command=writeCallback)
write_button.place(anchor="nw", relx=0.6, rely=0.7, relwidth=0.37)
check_checkbox = ttk.Checkbutton(win, variable=check)
check_checkbox.configure(text='Проверить')
check_checkbox.place(anchor="n", relx=0.5, rely=0.8)
status_label = ttk.Label(win, textvariable=status)
status_label.place(anchor="center", relx=0.5, rely=0.91)
win.mainloop()