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