Доступ к портам с использованием WinApi и dll из Python

или взлет на рожденном ползать


Предыдущие реализации на Питоне аналога проприетарного софта, оказались вполне ничего себе, т. е. все работает, и все похоже на Excel, а значит endusers будут довольны-). Дальнейший простор для полета фантазии немного сдерживается невозможностью доступа к более детальным настройкам портов из стандартных модулей. Но оказывается в Питоне есть такая изумительная вещь как доступ к WinApi функциям, что расширяет перспективы чуть ли не до Cpp-шных. Причем это касается не только портов, а еще кучи всяких вещей. Список и краткое описание поддерживаемых WinApi функций для Питона здесь-
docs.activestate.com/activepython/3.3/pywin32/win32file.html, прорва примеров на Nullege здесь — nullege.com/codes/search/win32file и здесь www.programcreek.com/python/index/1133/win32file, ну и как это принято в Питоне избавление от несовершенства модулей — доступ к WinApi функциям, осуществляется через модуль-) — win32file,- соответственно — pip install win32file.
Вот например как может выглядеть настройка и работа Com порта —
сначала понятно импортируем модуль:

import win32file

создаем хэндл:

hFile = win32file.CreateFile(«COM2», win32file.GENERIC_READ | win32file.GENERIC_WRITE, 0,None,win32file.OPEN_EXISTING, 0,None)

тут разница в названиях — стандартным предшествует 'win32file.', и вместо NULLNone
(в названии порта кавычки верхние см. рис. и комменты).
Далее аналогично стандартным процедурам, например в Visual Studo — структура настройки — создаем:

comDCB = win32file.DCB()

настраиваем:

comDCB.ByteSize = 8
comDCB.Parity = win32file.NOPARITY
comDCB.StopBits = win32file.ONESTOPBIT
comDCB.BaudRate = 9600

записываем:

win32file.SetCommState(hFile,comDCB)

рамер входного/выходного буфера:

win32file.SetupComm(hFile, 4096, 4096)

вот например маска(1) по приему первого байта, то чего нет в стандартном модуле pyserial:

win32file.SetCommMask(hFile, 1)

соответственно event:

win32file.WaitCommEvent(hFile, None)

ну и чтение и запись-

buff='пишем чего-нибудь-)'

не забываем, что передаем поток байт — переконвертируем строку в массив байт:

ttt=bytearray(buff, encoding='cp1251')

отправляем:

win32file.WriteFile(hFile,ttt,None)



ждем эвента по приему первого байта и читаем 19 байт:

win32file.WaitCommEvent(hFile, None)
win32file.ReadFile(hFile, 19)




ну и закрыть:

win32file.CloseHandle(hFile)

C LPT все немного по-другому, хотя и можно создать хэндл как показано выше — запись и чтение в файл повидимому приведет к обращению к стандартному драйверу и невозможности записи без получения 'ASK' от 'принтера'. Поэтому, как мне кажется, более удобное — широко известное решение — inpout32.dll.
Для возни с параллельным портом я использую следующий гипердевайс-

по мотивам того, который был представлен на pcports — www.kernelchip.ru/pcports/PS005.php Повторять мне было лень, я сделал проще — задействованы два регистра из трех — чтения нет(регистр Status не задействован). Питание светодиодов счетверенного семисегментного индикатора от высокого состояния одного порта и низкого состояния другого. Такая схема дает возможность использовать динамическую индикацию и оценить быстродействие разных способов вывода через порт, но… не будем забегать вперед-) Итак, пока просто выведем циферку скажем '7' на второй индикатор:
from ctypes import windll
p = windll.LoadLibrary(«C:\Python34\DLLs\inpout32.dll»)
p.Out32(890, 9)
p.Out32(888, 241)


(в названии пути кавычки верхние см. рис. и комменты)



функция возвращает 1 — типа все нормально-)



тут только одно тонкое место, часть выводов в регистре 'Control' инвертирована — ru.wikipedia.org/wiki/Параллельный_порт и это надо учитывать.

Ну и в заключение небольшой сравнительный тест результаты которого меня просто поразили. Две аналогичные программы — на старом VC6 и на Python — функционально это динамическая индикация через LPT порт. В VC6 в обработчике таймера производится пересчет(преобразование) числа в первом окошке в семисегментный код, во втором окне — задается время переключения (таймера). Запуск преобразования кнопка 'Start indicate', остановка — 'Stop'.



В Питоне все аналогично, но таймеров там нет и задержка реализована функцией time.sleep(timer) где timer — время в секундах, в VC6 время задается в миллисекундах. В Питоне в отдельном потоке функции вывода и задержки в бесконечном цикле, в VC функция вывода(такая же как в Питоне) в обработчике таймера, — по каждому срабатыванию выводит по одной цифре. Ну в общем все как обычно, и программная реализация самостоятельного интереса не представляет. А представляет интерес результат — скорости вывода( на вид естественно ) идентичны (!!!), т. е. шустрый питонский байт код, в этих конкретных условиях вполне такой же быстрый как и сишный компиллированный. Вообще-то из-за того что таймер в VC6 вообще нельзя поставить меньше 10 мс(поставить-то можно, толку не будет), а в Питоне задержку можно и 0.001 с, то Питон еще и быстрее, что собственно и видно при последнем включении с задержкой 0.001 с. на видео. Да, и отдельно прошу извинить за качество фоток и видео— снимал телефоном, более того когда снимал, если кто обратил внимание)), задержка в питонской программе стояла в два раза больше чем в сишной (т.е смена цифр была в два раза медленнее — забыл убрать вторую задержку во время отладки — сейчас конечно убрал), будет возможность видео пересниму. Если, что исходник питонской программы внизу, и заодно видео файлом.
P.S. Добавляю ссылку на видео — переснять не получится — слегка спалил LPT порт -(
  • 0
  • 16 мая 2016, 08:07
  • basil
  • 2
Файлы в топике: lpt_.zip, Test_python_vs_cpp_lpt.zip

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

RSS свернуть / развернуть
а вот интересно, скорость COM порта больше 115200, например 921600 можно тут в Питоне получить?
+1
А что действительно интересный вопрос, хз надо пробовать.
0
Слушайте, ну спасибо за вопрос оказалось не просто уместно, а просто то что надо)))
обалдеть, наверное и другие скорости доступны.
P.S. Кстати, почему-то в хэндле на странице не (кавычки)Com2(кавычки), а «COM2» так, что если кто будет копипастить будьте внимательнее.
Правильно почему-то не ставится.
P.P.S. Передача была через виртуальный порт, что живьем будет надо смотреть.
0
Можно, если железяка поддерживает. Доступ тут через стандартные функции WinAPI, так что работает все так же, как и в любой другой программе работы с компортом для винды.
0
К сожалению в виртуальных портах скорость в питоне ставиться любая, а вот в реальных только до 115,2 -(( причем в любимом всеми нами терминале br@y скорость ставиться и работает и 256 т.е. это какое-то ограничение в питоне — надо разбираться…
0
Это странно. Возможно, br@y просто не индицирует, что скорость не поставилась? По идее и там, и там используется одна и та же функция WinAPI и результат должен быть идентичен.
Можешь попробовать мой терминал, он точно проверяет, удалось ли выставить скорость (правда, если выставить неподдерживаемую скорость при открытом порту — вылетит).
0
Да, и конечно я принимал на второй экземпляр его же(br@y) т.е. скорости во всяком случае были одинаковые… Любопытно, а как твой проверяет фактическую скорость, есть какой-то механизм?
0
В питоне в консоли при попытке поставить в настройках(см скриншот выше) в реальном порту значение выше 115200 -вылетает неправильный аргумент.
0
Мой просто проверяет код возврата SetCommState.
Да, и конечно я принимал на второй экземпляр его же(br@y) т.е. скорости во всяком случае были одинаковые…
А они случаем не оба были на одинаковых аппаратных портах, которые в питоне выше 115к не ставятся?
В питоне в консоли при попытке поставить в настройках(см скриншот выше) в реальном порту значение выше 115200 -вылетает неправильный аргумент.
Это код возврата/исключение/нечто подобное, выбрасываемое функцией win32file.SetCommState? Если так, то вероятно оно так форвардит ошибку, возвращенную WinAPI.
0
А они случаем не оба были на одинаковых аппаратных портах, которые в питоне выше 115к не ставятся?
Не-не, понятно на разных железных портах и на скорости 256000 длинные строчки беспадобно без ошибок отлетали.
По-моему, я вообще строчки туда-сюда гонял, т.е. оба порта били прием/передача. Вообще ладно, — ты меня убедил еще раз все перепроверю и с твоей утилитой тоже.
0
Что значит «на разных»? На двух отдельных машинах? Может, просто с обеих сторон выставилась скорость 115к, хотя br@y считал, что поставил 256к?
0
Что значит «на разных»?
Com1 и Com2 на одной машине соединенные коротким кабелем(три проводка-))
Может, просто с обеих сторон выставилась скорость 115к
может, но радиобаттон стоял на 256 и там и там.
0
Протестировал br@y. Он просто игнорирует ошибку при выставлении скорости. С COM1, выставленного бреем на «256к» данные прекрасно принялись на COM2, открытый в моем терминале на 115.2к. Так что у тебя все работает правильно, ком-порт просто не поддерживает скорость выше 115.2к.
0
Прикольно, посмотрю еще у себя, довольно интересная ситуация. Вообще, спасибо что не ленишься-), просто здорово как много интересных и полезных вопросов благодаря обсуждению удалось затронуть.
0

Мда, видимо ты прав-) Действительно не все деревья растут до неба.
0
Собсно жаль, что железки не поддерживают 115.2к, но тем более величие питона!!))
0
не поддерживают 115.2к
В смысле > 115.2к -)
0
Вообще-то из-за того что таймер в VC6 вообще нельзя поставить меньше 10 мс(поставить-то можно, толку не будет), а в Питоне задержку можно и 0.001 с, то Питон еще и быстрее, что собственно и видно при последнем включении с задержкой 0.001 с. на видео.
То, что можно выставить меньшее число, вовсе не значит, что из-за этого работать будет быстрее. Дело в том, насколько криво написана программа, а не в тормознутости VC6.

P.S. Видео не нашёл.

З.Ы. Видео нашёл. Есть же YouTube, зачем в аттачи?)
0
То, что можно выставить меньшее число, вовсе не значит, что из-за этого работать будет быстрее. Дело в том, насколько криво написана программа, а не в тормознутости VC6.
Да само собой, насколько я помню MS не рекомендует ставить таймеры меньше 10 мс( ну и не дает возможности их использовать соответственно), мотивируя что будет неточно. Это связано видимо с не реальтаймовостью Windows. Ну и соответственно, насколько
питонская задержка действительно 1мс это надо смотреть конечно. Но на вид когда задержка 0.001 с работает явно быстрее(мерцание убирается)
P.S. Да аккаунта не завел, может пришло время заводить...-)
0
работает явно быстрее
по сравнению с 10 мс.
0
Да само собой, насколько я помню MS не рекомендует ставить таймеры меньше 10 мс( ну и не дает возможности их использовать соответственно), мотивируя что будет неточно.
Смотря какие таймеры ты используешь. Если SetTimer, который сыпет мессаги в окно — там разрешение упирается в квант времени, выделяемый виндой задаче (130мс), с поправкой на приоритет и загрузку системы. Если мультимедийный таймер (timeSetEvent) — его разрешение определяется частотой системного таймера (систика), по дефолту это 100Гц, но можно изменить вызовом какой-то функции (естественно, с общесистемным эффектом и ростом затрачиваемого на планировщик процессов времени). Если не отдавать квант времени системе, то можно висеть в delay-цикле, тут доступно разрешение до долей наносекунды (длительность такта процессора). Видимо, в питоне как раз delay-цикл.
0
Ой как интересно, надо будет как-нибудь порыться.
0
А чем pyserial не устроил для КОМ порта? Кросплатформенный, простой в использовании, дефакто сандарт для дёргания КОМ порта из питонячих скриптов.
0
В официальной его документации, я при всем желании не смог найти ни эвентов ни масок, собственно да и всех остальных DCB настроек msdn.microsoft.com/ru-ru/library/windows/desktop/aa363214(v=vs.85).aspx, если вы их нашли покажите, буду вам признателен)
0
да и всех остальных DCB настроек
ээ… основные-то есть понятно)
0
Не хватает важной информации:
На каких Windows проводились эксперименты и для каких это подходит?
ХР/W7x32/W7x64/W8/W10?
0
К величайшему сожалению у меня под руками сейчас только XP pro. Если кто-то попробует еще на чем-то буду очень рад)
0
В любой винде, которая поддерживает Win32 и в которой запустится питон. Учитывая, что Win32 поддерживается на всем начиная с Windows 95 — определяющим фактором будут требования питона.
0
Поправка: это относится только к COM-порту. Насчет LPT — надо смотреть требования inpout.dll. Впрочем, он поддерживает все от 95 до 8, а возможно и 10.
0
Да не может быть, неужели новые форточки позволяют ее использовать? Вы не в курсе кто-нибудь использовал эту библиотеку на 7/8/10?
0
Прошу прощения не понял, вы имели видимо ввиду питон, а насчет dll вы не в курсе?
0
Позволяют, по крайней мере та версия, что я нашел. Там и подписанный драйвер для 64-битных есть, чтобы не трахаться с дебаг-мод. От некоего Highrez.
0
Имеется в виду известные мнения о том, что типа 7, 8, 8.1 по другому работают с железом, старые дрова на них не идут и т.п. в свете того, что старые дрова действительно часто не идут, библиотека-то старая, она-то пойдет?
0
А нашел…
0
На ХР всё было проще и не надо было задумываться о подписанных драйверах.
Но 16 ГБ оперативки и ХР несовместимы :) и многие программы уже не поддерживают работу в ХР :(
Зато можно сосредоточиться на проработке USB переходников интерфейсов, которые воткнутся в любую ОС.
0
В 9х было еще проще, можно лезть в порты прямо из программы. Драйвер? Не нужен.
0
Спасибо!
0
Вместо «COM2» лучше открывать "\\.\COM2". Первый вариант оставлен исключительно ради совместимости с DOS и не работает с портами выше COM9.
+1
  • avatar
  • Vga
  • 16 мая 2016, 11:26
Кстати к сожалению нет. Ни так ни так чего-то не открывает, имеются в виду порты > 9(((
0
Странно должно. А в питоне знак “\” в строке это часом не спец символ, как а С, который нужно маскировать ("\\\\.\\COM10")?
0
У меня открывает. Ты не забыл про экранирование? В С/С++, например, строку надо задавать как "\\\\.\\COM2".
0
Да, все точно когда "\\\\.\\COM%d" тогда действительно все в порядке, автоматически скопипастил из поста)) Спасибо! Надо было мне быть внимательнее.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.