Форум сайта python.su
0
Всем доброго времени суток. Имеется проблема с COM-портом. Точнее с установкой уровней на контактах DTR, RTS.
Имеется устройство которое маршрутизирует сигналы передаваемые с компьютера по RS232 на другие устройства в зависимости от адреса, задаваемого с помощью линий DTR, RTS.
Т.е. чтобы передать посылку одному устройству нужно установить DTR = 1, RTS = 0 и отправить в порт посылку. Для другого устройства соответственно (DTR = 0, RTS = 1).
Проблема в том, что во время выполнения программы уровень DTR у меня устанавливается на короткий промежуток времени, затем сбрасывается в 0 и после этого отправляется посылка. Никак не могу разобраться почему так происходит и как перманентно установить уровень на линии DTR (и на линии RTS). Нагуглил только информацию по второму питону, но там устаревшие команды, которые из pyserial были упразднены. Складывается впечатление что в третьем Питоне это нереально. Может кто-нибудь сталкивался с такой проблемой?
Моя функция которая производит запись в порт.
[code python]
def serial_tx(parcel):
try:
ser = serial.Serial(combo.get(), 9600, timeout = 1)
if parcel == 2:
ser.dtr(1)
ser.rts(0)
time.sleep(0.5)
parcel_send = '812000065670'
parcel_full = bytes.fromhex(parcel_send)
ser.write(parcel_full)
serial_rx(parcel)
elif parcel == 1:
ser.rts = 1
ser.dtr = 0
time.sleep(0.5)
parcel_send = '8116052033'
parcel_full = bytes.fromhex(parcel_send)
ser.write(parcel_full)
except Exception:
lbl_error_com = Label(lbl_rx_data_dc, text = "Не удалось открыть COM-порт\nПовторите попытку снова\n \n ", foreground = 'red')
lbl_error_com.place(x=5, y=5)
[/code]
Офлайн
857
Используй методы правильно
Для установки методы .setRTS() и .setDTR(), а для чтения атрибуты rts и dtr.
>>> import serial >>> >>> obj = serial.Serial() >>> obj.rts True >>> obj.dtr True >>> obj.setRTS(False) >>> obj.rts False >>> obj.setDTR(False) >>> obj.dtr False >>> obj.setRTS(True) >>> obj.rts True >>> obj.setDTR(True) >>> obj.dtr True >>>
>>> b'\x81\x20\x00\x06\x56\x70' b'\x81 \x00\x06Vp' >>> bytes([0x81, 0x20, 0x00, 0x06, 0x56, 0x70]) b'\x81 \x00\x06Vp' >>>
Отредактировано py.user.next (Июнь 1, 2020 06:30:35)
Офлайн
0
Спасибо за наводку. Поправил в программе, но эффект остался. Хотя иногда срабатывает как надо, по крайней мере в comport1.1 Гифка
Офлайн
857
Полный код скинь сюда.
Офлайн
0
Сегодня уже уехал с работы, теперь в среду только попаду туда, отпишусь.
PS: Байтовые литералы не использую, поскольку документация и логи в таком виде у меня, проще ориентироваться. На сколько понимаю, это не критично?!
Офлайн
857
JavisПридёт время, когда надо будет формировать запросы динамически. А в питоне со списками больше возможностей для работы, чем со строками.
На сколько понимаю, это не критично?!
>>> b = bytearray([1, 2, 3, 4, 5]) >>> b bytearray(b'\x01\x02\x03\x04\x05') >>> b[0] = 4 >>> b bytearray(b'\x04\x02\x03\x04\x05') >>> b.pop() 5 >>> b bytearray(b'\x04\x02\x03\x04') >>> b.append(5) >>> b bytearray(b'\x04\x02\x03\x04\x05') >>>
JavisПодгонять код под что-нибудь вроде документации или логов - последнее дело. Код должен быть сделан максимально качественно. Логи должны быть сделаны максимально качественно. Документация должна быть сделана максимально качественно. И вот качественность одного не равна качественности другого, а качественность другого не равна качественности третьего. Поэтому и нужно делать “переходники”. То есть код выглядит одним образом, а когда его нужно вывести в лог, информация передаётся “переходнику”, который преобразует эту информацию максимально красиво для лога. Таким образом, у тебя и код хороший, и лог хороший.
Байтовые литералы не использую, поскольку документация и логи в таком виде у меня
Офлайн
0
Спасибо за замечание. Действительно со списками работать гораздо удобнее, уже столкнулся с этой проблемой. Код программы ниже.
[code python]import tkinter.ttk as ttk
from tkinter import *
from tkinter.ttk import Combobox, Button, Radiobutton, Label, Entry, Spinbox
import serial
import time
import winreg
version_reply = '81161720434'
speeds = ['1200','2400', '4800', '9600', '19200', '38400', '57600', '115200']
#Функция записи данных в реестр (последняя отправленная посылка, выбранный COM-порт)
def winreestr_push(comport):
software_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, 'Software')
winreg.CreateKey(software_key, 'dev3000')
rsmon_key = winreg.OpenKey(software_key, 'dev3000', 0, winreg.KEY_ALL_ACCESS)
winreg.SetValueEx(rsmon_key, "last_com" , None, winreg.REG_SZ, comport)
winreg.CloseKey(rsmon_key)
#Функция получения данных из реестра (последняя отправленная посылка, выбранный COM-порт)
def winreestr_pull():
try:
rsmon_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, 'Software\\dev3000')
comport = winreg.QueryValueEx(rsmon_key, "last_com")
except Exception:
comport = ("COM1", 1) # при получении из реестра значения ключа получаем похожий кортеж
return (comport)
#Функция получения данных из реестра (если стоит программа)
def winreestr_pull_company():
try:
rsmon_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, 'Software\\company\\3Ph_pm\\device_v4.23')
comport = winreg.QueryValueEx(rsmon_key, "device_COM")
except Exception:
comport = ("COM1", 1) # при получении из реестра значения ключа получаем похожий кортеж
combo.set(comport[0])
#Функция находит все свободные COM-порты в системе и добавляет их в список result
def serial_ports():
ports = ['COM%s' % (i + 1) for i in range(256)]
result = []
for port in ports:
try:
s = serial.Serial(port)
s.close()
result.append(port)
except (OSError, serial.SerialException):
pass
return result
#Функция проверяет состояние выбранного COM-порта
def com_port_state(ser):
ser = serial.Serial(combo.get(), 9600)
if ser.cd == True: # Если на линии обнаружен CD - рисуем зеленый квадрат
com_port_state = Canvas(window, width=10, height=10, bg = 'green')
com_port_state.place(x=310, y=40)
else:
com_port_state = Canvas(window, width=10, height=10, bg = 'red')
com_port_state.place(x=310, y=40)
#Функция отправляет посылку устройству
def serial_tx(parcel):
try:
ser = serial.Serial(combo.get(), 9600, timeout = 1)
if parcel == 2:
ser.setDTR(TRUE)
ser.setRTS(FALSE)
time.sleep(0.5) #задержка установлена временно для удобства отладки
parcel_send = '812000700656'
parcel_full = bytes.fromhex(parcel_send)
ser.dtr(True).write(parcel_full)
serial_rx(parcel)
elif parcel == 1:
ser.setRTS(TRUE)
ser.setDTR(FALSE)
time.sleep(0.5)
parcel_send = '8116330520'
parcel_full = bytes.fromhex(parcel_send)
ser.write(parcel_full)
serial_rx(parcel)
elif parcel == 3:
parcel_send = '8110200749FF'
parcel_full = bytes.fromhex(parcel_send)
ser.write(parcel_full)
serial_rx(parcel)
except Exception:
lbl_error_com = Label(lbl_rx_data_dc, text = "Не удалось открыть COM-порт\nПовторите попытку снова\n \n ", foreground = 'red')
lbl_error_com.place(x=5, y=5)
#Функция чтения данных из COM-порта (еще в доработке)
def serial_rx(parcel_tx):
try:
display_data_rx = ser.read(25) #читаем 20 байт данных с порта
parcel_hex = display_data_rx.hex() #Переводим полученные данные в HEX-формат (убираем /x)
print(parcel_hex)
parcel_rx_up = parcel_hex.upper() #Переводим все буквы в верхний регистр (для удобства)
lbl_parcel_rx = Label(lbl_rx_data_dc, text = " ")
lbl_parcel_rx.place(x=200, y=320)
lbl_parcel_rx = Label(lbl_rx_data_dc, text = parcel_rx_up) #Выводим в пользовательский интерфейс
lbl_parcel_rx.place(x=200, y=320)
ser.close()
rx_dc1 = rx_dc(parcel_rx_up, parcel_tx)
rx_dc1.crc_plata(parcel_rx_up)
except Exception:
lbl_error_com = Label(lbl_rx_data_dc, text = "Нет принятых данных\nПроверьте соединение\nи настройка COM-порта\n ", foreground = 'red')
lbl_error_com.place(x=5, y=5)
#Main program
window = Tk()
window.title("Test-Operator")
window.geometry('350x300')
btn_opros = Button(window, text="Из реестра", command = winreestr_pull_company)
btn_opros.place(x=210, y=35)
reestr = winreestr_pull() # присваиваем переменной кортеж значений (0-последняя посылка из реестра (кортеж), 1-последний выбраный COM-порт (кортеж))
lbl0 = Label(window, text = "Выберите COM-порт:").place(x=15, y=15)
combo = Combobox(window, values = serial_ports())
combo.place(x=15, y=35)
combo.bind('<<ComboboxSelected>>', com_port_state) #вызываем функцию отображения состояния порта
combo.set(reestr[0]) # устанавливае при запуске программы значение COM-порта из реестра
btn_send = Button(window, text="Посылка 1", width = 20, command = lambda: serial_tx(1))
btn_send.place(x=15, y=70)
btn_send = Button(window, text="Посылка 2 ", width = 20, command = lambda: serial_tx(2))
btn_send.place(x=15, y=110)
btn_stop = Button(window, text="Остановить", command = lambda: serial_tx(3))
btn_stop.place(x=15, y=150)
lbl_rx_data_dc = LabelFrame(window, text = "Принятые данные")
lbl_rx_data_dc.place(x=15, y=190, width = 300, heigh = 100)
window.mainloop()
[/code]
Офлайн
0
В распоряжении имеется сторонняя программа. Гифка с примером работы здесь
С ней устройства работают корректно. Но она не очень удобна для пользователей. Поэтому возникла идея написать что-то попроще с двумя кнопками.
PS: Для тестов на рабочем месте был собран шнурок (нуль модемный - картинка с интернета) COM-COM который связывает два COM-порта на одном компьютере COM1 и COM4.
Отредактировано Javis (Июнь 3, 2020 09:35:41)
Офлайн
857
JavisПередавай не TRUE и FALSE, а True и False. TRUE и FALSE взяты из модуля tkinter, который никак не относится к pyserial.if parcel == 2: ser.setDTR(TRUE) ser.setRTS(FALSE) time.sleep(0.5) #задержка установлена временно для удобства отладки parcel_send = '812000700656' parcel_full = bytes.fromhex(parcel_send) ser.dtr(True).write(parcel_full) serial_rx(parcel)
JavisУстанавливай DTR через метод .setDTR(). Этот метод для того и сделан, чтобы через него устанавливали значение.ser.dtr(True).write(parcel_full)
JavisНе создавай метки (Label) динамически. Особенно это в tkinter нельзя делать, так как он работает через одно место. Это может приводить к таким эффектам, что у тебя устройство пришлёт что-нибудь, а tkinter покажет вообще что-то другое и ты будешь думать, что дело в устройстве, а дело будет в какой-нибудь динамически сгенерированной хрени в графическом интерфейсе.except Exception: lbl_error_com = Label(lbl_rx_data_dc, text = "Не удалось открыть COM-порт\nПовторите попытку снова\n \n ", foreground = 'red') lbl_error_com.place(x=5, y=5)
Отредактировано py.user.next (Июнь 3, 2020 20:17:57)
Офлайн
0
py.user.nextЭто реально тупая опечатка была.
Передавай не TRUE и FALSE, а True и False.
py.user.nextЭто я уже от безысходности наваял, когда пробовал и так и этак.
Устанавливай DTR через метод .setDTR().
py.user.nextУчту на будущее. Правда, наиболее красиво для пользователя, на мой взгляд, через label получается, хоть и приходится костыли городить часто.
Не создавай метки (Label) динамически
py.user.nextА вот тут началось самое интересное. Стоит отметить, что заранее я установил и использовал программу COM Port Toolkit для отслеживания обмена пакетами между устройствами. Эта прога позволяет слушать COM-порт, не занимая при этом его.
В общем, сделай всё в консоли сначала.
Отредактировано Javis (Июнь 5, 2020 05:27:10)
Офлайн