Здравствуйте товарищи!
В статье хочу поделиться с вами своим опытом по применению стандартных команд для программируемых инструментов (SCPI). Рассмотрим, как подключиться к установке проверки электрической безопасности GWINSTEK GPT-79803 и автоматизировать измерения и запись результатов.
Предприятие, на котором я работаю, занимается разработкой и производством интересных штук. В том числе специальных соединителей. Соединитель представляет собой кабель с разъемами на концах. К соединителю есть ряд требований и одно из них - сохранять работоспособность в условиях повышенного давления под водой. Соединители были разработаны и изготавливаются. Каждый соединитель в обязательном порядке проходит испытания повышенным давлением в воде. Тут и появляется наш прибор. Он может подать на исследуемые компоненты большую разность потенциалов и измерить сопротивление между ними (например, между чётными и нечётными проводниками в кабеле соединителя). Если в конструкции где-то нарушается герметичность (продавливается вода), то прибор покажет изменение сопротивления и, в зависимости от настроек, тест будет либо пройден либо нет.
Процесс испытания проходит следующим образом:
сопротивление замеряется в нормальных условиях;
потом постепенно повышается давление и на определенных отметках замеры повторяются;
каждый замер документируется путём фотографирования экрана, скачивания фоток и занесения их в протокол испытаний.
На этом моменте мой коллега устал и спросил: "А можно ли как-то сделать, что бы результаты сразу в компьютере появлялись?". "Если на задней панели есть гнездо разъема USB type A, то, наверное, можно" - ответил я.
Подключаем прибор к компьютеру. В менеджере устройств он определяется как "конвертер USB COM CP210X". Настраиваем соединение и подключаемся терминалом. Соединение устанавливается ... и всё. Немигающий зеленый курсор, ничего не появляется и ничего не вводится. Самое время заглянуть в документацию. (На самом деле всё вводилось и появлялось, но об этом позже, чтобы сохранить линейность произошедшего)
Ответов в рисунке выше найдено не было, дальнейший поиск шел на сайте производителя. И, что интересно, прибора с наименованием GPT-79803 на официальном сайте Gwinstek я не нашел. Но там были GPT-9803 c документацией и программами. Скорее всего прибор в моей компании был изготовлен по спецзаказу (русские буквы на рамке экрана и 7 в начале номера модели на это указывают, но это не точно).
Программа была загружена и установлена. По меню и главному окну понятно, что все необходимые функции реализованы. Бери и пользуйся.
Но при попытке подключения выводилось окно с перечислением приборов к которым можно подключиться (естественно все индексы приборов были без семёрки).
С одной стороны опять неудача, с другой - программа как-то получила идентификатор моего прибора, сравнила его со своим списком разрешенных в Российской Федерации моделей, не нашла его там и отказала (наверное, санкции). Обмен данными происходит. Значит надо подсмотреть, почему программа обменивается данными, а через терминал не получается.
Для сниффинга USB использовалась программа USBPcap. Это консольное приложение позволяет перехватить обмен по USB и записать его в формате pcap. Этот формат можно открыть программой Wireshark. Процесс происходит следующим образом:
запускается USBPcap;
USBPcap выдает список подключенных устройств, цифрой выбираем интересующее устройство и вводим название файла для записи;
рядом открывается окно программы прибора;
в USBPcap начинаем запись, в программе прибора проводим процедуру подключения;
-
останавливаем запись, в Wireshark открываем полученный файл.
На рисунке ниже находим первую команду, которая была отправлена от хоста к устройству
Странно, ведь в руководстве оператора написано *IDN? (по этой команде прибор отвечает своим наименованием). Смотрим дальше.
А вот и интересующий ответ ниже.
Дальше исследования переместились в Юпитер блокнот. Мне удобно проверять элементы кода в нем и смотреть вывод на разных этапах. Для попыток подключения к прибору использовалась библиотека pyvisa, но подключиться так и не удалось (хотя наличие прибора определялось).
На этом этапе возникла идея, что при попытке подключения терминалом просто не выводилось эхо вводимых символов, а при попытке ввода команд *CLS *IDN? в слепую - прибор ответил (потом я включил эхо в настройках Putty и в терминале всё стало выглядеть ожидаемо). Можно смело вернуться в Юпитер блокнот и организовать обмен с прибором, на этот раз через pyserial. Методом проб было выяснено, что в конце команды надо добавлять символ новой линии и перевода каретки.
Команды для работы именно с этим прибором были найдены на официальном сайте (естественно для 9000 серии). Когда диалог налажен, можно спросить у прибора текущий режим и запустить измерение.
Для ускорения внедрения рационального предложения было решено написать небольшое приложение с использованием pyQt (ну не понравится моему коллеге работать с Юпитер блокнотом).
П р и м е ч а н и е - Так-то я не программист, поэтому ниже представленный код можно и нужно критиковать.
Для создания интерфейса решено было использовать Qt Designer. Нужно всего лишь добавить...(с) QListWidget, QComBobox, QPushButton. Размещены элементы на вертикальном макете (Vertical Layout) - при масштабировании окна элементы будут масштабироваться автоматически. Сохраняем интерфейс (я назвал файл design.ui) и конвертируем ui файл в py файл. В составе pyQt для этого есть программа pyuic6. Конверсия происходит вызовом программы pyuic6 design.ui design.py из каталога с программой. Рекомендуется добавить путь к pyuic6 в переменную PATH, у меня путь выглядел примерно так - C:\Users\......\AppData\Roaming\Python\Python312\Scripts.
Во избежание зависания интерфейса программы, функция измерения помещается в поток. Ещё мне захотелось изменять цвет кнопки во время измерения и оказалось, что из потока нельзя менять ui. Вопрос решается через механизм сигналов. Ниже представлен код программы.
import sys # sys нужен для передачи argv в QApplication
from PyQt6 import QtWidgets
from PyQt6.QtCore import pyqtSignal, QObject, pyqtSlot
import serial.tools
import serial.tools.list_ports
import design # Это наш конвертированный файл дизайна
import time
import serial
from threading import *
import datetime as dt
class ExampleApp(QtWidgets.QMainWindow, design.Ui_MainWindow):
sendChangeColorBusy = pyqtSignal()
sendChangeColorReady = pyqtSignal()
def __init__(self):
# Это здесь нужно для доступа к переменным, методам
# и т.д. в файле design.py
super().__init__()
self.setupUi(self) # Это нужно для инициализации нашего дизайна
self.PB_Mesure.clicked.connect(self.thread) # Запуск потока по кнопке
# Изменение цвета кнопки
self.sendChangeColorBusy.connect(
lambda: self.PB_Mesure.setStyleSheet('background-color: red; color: yellow'))
self.sendChangeColorReady.connect(
lambda: self.PB_Mesure.setStyleSheet('background-color: light gray; color: black'))
# Получаем список COM портов
ports=serial.tools.list_ports.comports()
# Пробум заполнить выпадающий список
try:
print(ports[0])
for port in ports:
self.comboBox.addItems(port)
except:
self.statusbar.showMessage('Прибор не обнаружен!
Проверьте подключение и перезапустите программу.')
# Процесс измерения запускается в потоке
def thread(self):
t1=Thread(target=self.measure,args=(self,))
t1.start()
# функция измерения
def measure(self,obj):
#Имя порта получаем из выпадающего списка
port = str(self.comboBox.currentText())
baudrate = 115200 # Скорость порта
ser = serial.Serial(port, baudrate=baudrate)
# Если удалось открыть порт, шлём команды и читаем ответы
if(ser.is_open == True):
# Запрос профиля измерения
message = b"*CLS\n\r"
ser.write(message)
message = b"MANU15:EDIT:SHOW ?\n\r"
ser.write(message)
data = ser.readline()
profile=data.decode("utf-8")
# Вывод профиля в статус бр
self.statusbar.showMessage('Профиль >>> '+profile.strip("\r\n"))
# Команда на начало измерения
message = b"*CLS\n\r"
ser.write(message)
message = b"FUNC:TEST ON\n\r"
ser.write(message)
# Сигнал изменения цвета кнопки
obj.sendChangeColorBusy.emit()
# Цикл ожидания измерения. Нужно как-то по другому реализовать
for i in range (33):
self.PB_Mesure.setText('Внимание, идет измерение! '+str(i)+'c')
time.sleep(1)
# Запрос результата измерения
command = b'*CLS\n\r'
ser.write(command)
command = b'MEAS?\n\r'
ser.write(command)
data = ser.readline()
# Ответ приходит в виде массива байт, конвертируем в строку
answer=data.decode("utf-8")
# Выводим ответ
self.listWidget.addItem(dt.datetime.now().strftime("%H:%M:%S")
+' '+answer.strip("\r\n"))
# Сигнал на изменение цвета кнопки
obj.sendChangeColorReady.emit()
ser.close()
self.statusbar.showMessage('Готов')
self.PB_Mesure.setText('Измерить!')
# Запись измерения в файл
with open("test.txt", "a") as myfile:
myfile.write(dt.datetime.now().strftime("%H:%M:%S")
+' '+answer.strip("\r"))
else:
self.statusbar.showMessage('Порт занят!')
def main():
app = QtWidgets.QApplication(sys.argv) # Новый экземпляр QApplication
window = ExampleApp() # Создаём объект класса ExampleApp
window.show() # Показываем окно
app.exec() # и запускаем приложение
if __name__ == '__main__': # Если мы запускаем файл напрямую, а не импортируем
main() # то запускаем функцию main()
Уже глядя на код в статье появились такие мысли:
получать время ожидания из профиля прибора, и как-то по таймеру опрашивать статус измерения;
выделить отдельно функцию отправки команд и реализовать обработку ошибок;
-
после подключения к прибору проверять действительно ли это целевой прибор.
Ниже представлены снимки экрана в процессе измерения и ожидания.
В результате проделанной работы получаем дополнительный инструмент для упрощения рутинной работы, опыт и инженерное наслаждение (от того, что заработало).
Дальнейшее развитие может быть следующим. В составе испытательной камеры есть цифровой манометр. Его можно также подключить и запускать автоматически измерение при достижении определенного давления. А дальше - pandas - pydocx - шаблон документа - готовый отчет. Уже прям на измерительный комплекс тянет!
Скрытый текст
Мне нравится читать о том, что делают увлеченные своим делом технари. Например такие: https://t.me/monobogdann, https://t.me/igornegodastartuem, https://t.me/alexgyvershow, https://t.me/dlinyj_news, https://t.me/brainfuckpc, https://t.me/DV_Workrshop.
И я решил создать свой канал. Там про моделирование, электронику и программирование. Возможно, вам будет интересно - https://t.me/modelistconstruktor
radioxoma
Не хотите на tkinter переписать? Нативный вид под виндой и нет тяжелой зависимости. Всё равно не используете QThreads и Qt-шную настройку для последовательного порта. Правда с tkinter трудно получать удовольствие.
avtopolet Автор
На выходе программка 1 мегабайт и папка 70 мегабайт. Можно перенести на флешке. Вы имеете в виду под тяжестью это? Tk будет меньше? Да, конечно это много. Я делал похожее на C++. Там программа управляет осциллографом, генератором и шаговиком, строит графики. Объем около 17 мегабайт. Но и делать сложнее.
radioxoma
Код привязан к версии Qt. PyQt распространяется под GPL - покупай лицензию или открывай исходный код. В плане дистрибуции теперь бинарные сборки есть на pypi, стало проще. Но раньше под виндой это была прям боль, а под Linux приходилось собирать PySide самому.
На Windows он устанавливается вместе с python.
Программа на 4 виджета может вполне обойтись стандартной библиотекой + pyserial и не множить сущности, я это имел в виду. У Вас будет одна зависимость - pyserial и то небинарная.