eddy_em: (Default)
eddy_em ([personal profile] eddy_em) wrote2020-11-20 04:45 pm

Сервер для пусироботовских драйверов

Допилил базовую часть сервера. Теперь нужно еще добавить кое-какой небольшой функционал, и можно будет пилить "морду".

Наслушавшись от народа, как USB'шные железяки имеют свойства "отваливаться" (хотя, на БТА уже пару лет работает термомониторинг главного зеркала, таких проблем не было), добавил возможность переподключаться в случае чего (главное — не втыкать в USB других устройств с такими же VID/PID, как у CAN-трансивера; в моем случае это — устройства, являющиеся PL2303 или выдающие себя за них). Если что-то пошло не так, в течение пяти секунд сервер будет пытаться переподключиться. Если же это не удастся, он благополучно сдохнет, и "родительский надзор", заметив это, перезапустит его заново...
Просто сделать сервер для конкретной железяки не хотелось: потому что в этом случае достаточно сложно будет это к другой железке приспособить, да и теряется возможность мониторинга CAN-шины. Поэтому перво-наперво я обеспечил возможность, не мешая основным потокам, мониторить сообщения в "чистом CAN" или CANopen, а также что-нибудь в шину отправлять.
Сообщения передаются в текстовой форме через сокет. Для обеспечения безопасности сокет открыт только локально. А уже "мастер-сервер" будет проверять доступ и т.п. Ну или можно по SSH команды отправлять неткатом.
Как и в случае микроконтроллеров, я заложил возможность работы в консоли нетката, поэтому есть команды help.
Скажем, если просто набрать help, получим список команд. Пока они такие:
help> help - show help
help> list - list all threads
help> mesg NAME MESG - send message `MESG` to thread `NAME`
help> register NAME ID ROLE - register new thread with `NAME`, raw receiving `ID` running thread `ROLE`
help> threads - list all possible threads with their message format
help> unregister NAME - kill thread `NAME`

Формат передаваемых сообщений простой: первым словом идет имя потока или какое-то ключевое слово (от имени потока отличается наличием знака ">" в конце, поэтому потоки лучше так же не именовать). За ним идет перечисление параметр=значение, разделенные пробелами.
Т.к. клиентов должно быть немного (один-два), то обычный poll() отлично справляется с мониторингом файловых дескрипторов сокетов. Если клиент "отвалился", то на его место в массиве структур поллинга втыкается последний клиент. И так далее...
Чтобы обеспечить гибкость относительно разнообразных железяк, каждым шаговым двигателем управляет один поток. Новый поток нужно "зарегистрировать" командой register. Ее параметры: имя потока (должно быть уникальным, иначе в ответ получи ошибку), идентификатор потока (классический CANbus ID, который поток будет слушать, скажем, если нам нужно подключиться к CANopen устройству с NodeID=10, то ID=0x58A) и роль потока.
Скажем, зарегистрируем два двигателя "x" и "y":
register x 0x58a stepper
OK
x maxspeed=OK
register y 0x58b stepper   
OK
y maxspeed=OK

Сообщение X maxspeed=OK вылезает из-за того, что сразу же после запуска потока соответствующий контроллер инициализируется значением максимальной скорости (потом его можно поменять).
Командой list можно посмотреть, какие потоки запущены:
list
thread> name='x' role='stepper' ID=0x58A
thread> name='y' role='stepper' ID=0x58B
thread> Send message 'help' to threads marked with (args) to get commands list

(последнее сообщение, наверное, в дальнейшем уберу).
Чтобы посмотреть, какие роли могут быть назначены потокам, можно дать команду threads:
threads
role> canopen NodeID index subindex [data] - raw CANOpen commands with `index` and `subindex` to `NodeID`
role> emulation (list) - stepper emulation
role> raw ID [DATA] - raw CANbus commands to raw `ID` with `DATA`
role> stepper (list) - simple stepper motor: no limit switches, only goto

(возможно, логичней ее будет в roles переименовать).
canopen дает возможность работать с CAN-шиной в режиме CANopen, посылая туда пакеты и читая, что приходит (контроль отправленных пакетов я пока не добавлял; но можно и это сделать). raw дает доступ к "чистой" CAN-шине. Еще надо будет добавить turret и linear — турель и линейную подвижку (с концевиками).
Чтобы посмотреть, какие команды можно послать определенному потоку (они посылаются при помощи mesg имя сообщение), посылаем ему команду help:
mesg x help
OK
x> COMMAND   NARGS   MEANING
x> stop        0     stop motor and clear errors
x> status      0     get current position and status
x> relmove     1     relative move
x> absmove     1     absolute move to position arg
x> enable      1     enable (!0) or disable (0) motor
x> setzero     0     set current position as zero
x> maxspeed    1     set/get maxspeed (get: arg==0)
x> info        0     get motor information

Скажем, нужна нам полная информация о шаговике, отправляем команду info:
mesg x info
OK
x errstatus=0
x devstatus=0
x curpos=0
x enable=1
x microsteps=32
x extenable=0
x maxspeed=3200
x maxcurnt=600
x gpioval=8191
x rotdir=1
x relsteps=0
x abssteps=0

Хотим переехать в абсолютное положение, пожалуйста:
mesg x absmove 3200
OK
x abssteps=OK
mesg x status
OK
x devstatus=0
x curpos=3200
x errstatus=0

Если во время движения попробовать дать новую команду, придет сообщение об ошибке, например:
mesg x relmove 1000
OK
x abortcode='0x8000022' error='Data cannot be transferred or stored to the application because of the present device state'
x abortcode='0x8000022' error='Data cannot be transferred or stored to the application because of the present device state'

Здесь их два, т.к. команда relmove сначала пытается задать направление движения, а затем — указать, сколько шагов надо проехать.
Все вот эти "OK" и некоторые другие сообщения (например, об ошибке) появляются лишь у клиента, отправляющего данные. Все остальные видят лишь общий вывод, который можно фильтровать по имени потока:
x> COMMAND   NARGS   MEANING
x> stop        0     stop motor and clear errors
x> status      0     get current position and status
x> relmove     1     relative move
x> absmove     1     absolute move to position arg
x> enable      1     enable (!0) or disable (0) motor
x> setzero     0     set current position as zero
x> maxspeed    1     set/get maxspeed (get: arg==0)
x> info        0     get motor information

x abssteps=OK
x rotdir=OK
x relsteps=OK

Так как в С нет ООПщины, а все эти турели и т.п. железки по сути — наследники "простого шаговика", то анализ команды пользователя происходит постепенно: сначала она прогоняется через "базовый анализатор", а если тот ничего не нашел, то анализируется уже по специфичным командам. Например, если добавить концевики, получим турель для одного концевика или линейную подвижку для двух (в принципе, у линейной тоже может быть лишь один концевик, но ее положение задается отсчетами шагов, а у турели — номером позиции, т.е. понадобятся дополнительные команды).

Post a comment in response:

This account has disabled anonymous posting.
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting

If you are unable to use this captcha for any reason, please contact us by email at support@dreamwidth.org