Здравствуйте товарищи!

В статье хочу поделиться с вами своим опытом по применению стандартных команд для программируемых инструментов (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 открываем полученный файл.

    На рисунке ниже находим первую команду, которая была отправлена от хоста к устройству

*CLS - команда очищения регистров событий и очереди ошибок
*CLS - команда очищения регистров событий и очереди ошибок

Странно, ведь в руководстве оператора написано *IDN? (по этой команде прибор отвечает своим наименованием). Смотрим дальше.

Кто ты, воин?
Кто ты, воин?

А вот и интересующий ответ ниже.

Я GPT-79803, сын Gwinstek'а!
Я GPT-79803, сын Gwinstek'а!

Дальше исследования переместились в Юпитер блокнот. Мне удобно проверять элементы кода в нем и смотреть вывод на разных этапах. Для попыток подключения к прибору использовалась библиотека pyvisa, но подключиться так и не удалось (хотя наличие прибора определялось).

Ошибка подключения при использовании pywisa
Ошибка подключения при использовании pywisa

На этом этапе возникла идея, что при попытке подключения терминалом просто не выводилось эхо вводимых символов, а при попытке ввода команд *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

Комментарии (4)


  1. radioxoma
    17.12.2024 06:42

    Не хотите на tkinter переписать? Нативный вид под виндой и нет тяжелой зависимости. Всё равно не используете QThreads и Qt-шную настройку для последовательного порта. Правда с tkinter трудно получать удовольствие.


    1. avtopolet Автор
      17.12.2024 06:42

      На выходе программка 1 мегабайт и папка 70 мегабайт. Можно перенести на флешке. Вы имеете в виду под тяжестью это? Tk будет меньше? Да, конечно это много. Я делал похожее на C++. Там программа управляет осциллографом, генератором и шаговиком, строит графики. Объем около 17 мегабайт. Но и делать сложнее.


      1. radioxoma
        17.12.2024 06:42

        Код привязан к версии Qt. PyQt распространяется под GPL - покупай лицензию или открывай исходный код. В плане дистрибуции теперь бинарные сборки есть на pypi, стало проще. Но раньше под виндой это была прям боль, а под Linux приходилось собирать PySide самому.

        Tk будет меньше?

        На Windows он устанавливается вместе с python.

        Программа на 4 виджета может вполне обойтись стандартной библиотекой + pyserial и не множить сущности, я это имел в виду. У Вас будет одна зависимость - pyserial и то небинарная.


  1. radioxoma
    17.12.2024 06:42

    (Ответил не в ту ветку.)