Merge branch 'amor-selene'

Conflicts:
	.gitignore
	sinqEPICSApp/src/pmacAxis.cpp
	sinqEPICSApp/src/pmacAxis.h
	sinqEPICSApp/src/pmacController.cpp
	sinqEPICSApp/src/pmacController.h
This commit is contained in:
2020-10-01 09:31:23 +02:00
11 changed files with 324 additions and 41 deletions

124
.gitignore vendored
View File

@ -1,3 +1,121 @@
O.*
.cvsignore
.gitignore
# Took these from the https://github.com/github/gitignore project on October 21, 2011
# **** 'Personal' entries don't belong in here - put them in your .git/info/exclude file ****
# Ignore text editor (e.g. emacs) autosave files
*~
# Compiled Object files
*.slo
*.lo
*.o
*.obj
*.d
SICServer*
# Compiled Dynamic libraries
*.so
# Compiled Static libraries
*.lai
*.la
*.a
# Compiled python files
*.py[co]
# Eclipse-generated files
*.pydevproject
.project
.metadata
bin/**
tmp/**
tmp/**/*
*.tmp
*.bak
*.swp
*~.nib
local.properties
.classpath
.settings/
.loadpath
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# PDT-specific
.buildpath
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
*.sln
*.vcproj
*.exe
*.vcxproj
*.filters
# User-specific files
*.suo
*.user
*.sln.docstates
*.sdf
#Test results
*.log
# Build results
[Dd]ebug/
[Rr]elease/
*_i.c
*_p.c
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.vspscc
.builds
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
# Visual Studio profiler
*.psess
*.vsp
# ReSharper is a .NET coding add-in
_ReSharper*
# Others
*.autosave
# Windows image file caches
Thumbs.db
# Mac OS X Finder
.DS_Store
._*

1
10-llbDevices.rules Normal file
View File

@ -0,0 +1 @@
SUBSYSTEM=="usb", ATTR{idVendor}=="04b4", ATTR{idProduct}=="1002", MODE="0666" GROUP="plugdev", TAG+="uaccess"

View File

@ -2,7 +2,7 @@
include /ioc/tools/driver.makefile
MODULE=sinq
LIBVERSION=koennecke
BUILDCLASSES=Linux
# additional module dependencies

View File

@ -0,0 +1,6 @@
# workaround over set position
record(ao, "$(P)$(M)-SetPosition") {
field(DTYP, "asynFloat64")
field(OUT, "@asyn($(PORT),$(N),1) SET_MOTOR_POSITION")
field(PINI, "YES")
}

View File

@ -10,8 +10,8 @@
#include "SINQController.h"
#include "asynMotorController.h"
SINQController::SINQController(const char *portName, const char *SINQPortName, int numAxes)
: asynMotorController(portName, numAxes+1, NUM_MOTOR_DRIVER_PARAMS+2,
SINQController::SINQController(const char *portName, const char *SINQPortName, int numAxes, const int& extraParams)
: asynMotorController(portName, numAxes+1, NUM_MOTOR_DRIVER_PARAMS+extraParams,
0, // No additional interfaces beyond those in base class
0, // No additional callback interfaces beyond those in base class
ASYN_CANBLOCK | ASYN_MULTIDEVICE,

View File

@ -17,7 +17,7 @@
class epicsShareClass SINQController : public asynMotorController
{
public:
SINQController(const char *portName, const char *SINQPortName, int numAxes);
SINQController(const char *portName, const char *SINQPortName, int numAxes, const int& extraParams=2);
friend class SINQAxis;
protected:

View File

@ -44,6 +44,9 @@
#define MULT 1000.
#define IDLEPOLL 2.
#define BUSYPOLL .05
#define ABS(x) (x < 0 ? -(x) : (x))
extern "C" void shutdownCallback(void *pPvt)
@ -56,9 +59,10 @@ extern "C" void shutdownCallback(void *pPvt)
}
// These are the pmacAxis class methods
pmacAxis::pmacAxis(pmacController *pC, int axisNo)
pmacAxis::pmacAxis(pmacController *pC, int axisNo, bool enable)
: SINQAxis(pC, axisNo),
pC_(pC)
pC_(pC),
autoEnable(enable)
{
static const char *functionName = "pmacAxis::pmacAxis";
@ -80,6 +84,7 @@ pmacAxis::pmacAxis(pmacController *pC, int axisNo)
status6Time = 0;
starting = 0;
homing = 0;
next_poll = -1;
/* Set an EPICS exit handler that will shut down polling before asyn kills the IP sockets */
epicsAtExit(shutdownCallback, pC_);
@ -156,13 +161,18 @@ asynStatus pmacAxis::getAxisInitialStatus(void)
}
// Enable the axis. After startup, the axis are disabled on the controller...
sprintf(command, "M%2.2d14=1", axisNo_);
cmdStatus = pC_->lowLevelWriteRead(axisNo_,command, response);
if (cmdStatus ) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s: Error: enaabling axis %d failed.\n", functionName, axisNo_);
return asynError;
}
// Warning: Selene lift axis should not be automatically enabled
if (autoEnable) {
sprintf(command, "M%2.2d14=1\n", axisNo_);
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "Enable axis %d: %s",axisNo_,command);
cmdStatus = pC_->lowLevelWriteRead(axisNo_,command, response);
if (cmdStatus ) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s: Error: enaabling axis %d failed.\n", functionName, axisNo_);
return asynError;
}
}
callParamCallbacks();
return asynSuccess;
@ -202,7 +212,9 @@ asynStatus pmacAxis::move(double position, int relative, double min_velocity, do
sprintf( command, "P%2.2d23=0 Q%2.2d01=%12.4f M%2.2d=1", axisNo_, axisNo_, realPosition, axisNo_);
status = pC_->lowLevelWriteRead(axisNo_,command, response);
next_poll = -1;
return status;
}
@ -224,6 +236,8 @@ asynStatus pmacAxis::home(double min_velocity, double max_velocity, double accel
status = pC_->lowLevelWriteRead(axisNo_,command, response);
homing = 1;
next_poll = time(NULL) + BUSYPOLL;
return status;
}
@ -246,7 +260,8 @@ asynStatus pmacAxis::setPosition(double position)
{
//int status = 0;
static const char *functionName = "pmacAxis::setPosition";
errlogPrintf("executing : %s\n", functionName);
pC_->debugFlow(functionName);
// Cannot do this.
@ -276,6 +291,11 @@ asynStatus pmacAxis::poll(bool *moving)
static const char *functionName = "pmacAxis::poll";
char message[132];
// Protect against excessive polling
if(time(NULL) < next_poll){
return asynSuccess;
}
sprintf(message, "%s: Polling axis: %d", functionName, this->axisNo_);
pC_->debugFlow(message);
@ -284,7 +304,13 @@ asynStatus pmacAxis::poll(bool *moving)
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"%s: getAxisStatus failed to return asynSuccess. Controller: %s, Axis: %d.\n", functionName, pC_->portName, axisNo_);
}
if(*moving){
next_poll = time(NULL) + BUSYPOLL;
} else {
next_poll = time(NULL) + IDLEPOLL;
}
callParamCallbacks();
return status ? asynError : asynSuccess;
}
@ -345,8 +371,8 @@ asynStatus pmacAxis::getAxisStatus(bool *moving)
int done = 0, posChanging = 0;
double position = 0;
int nvals = 0;
int axisProblemFlag = 0;
epicsUInt32 axErr = 0, axStat = 0, highLim = 0, lowLim= 0;
int axisProblemFlag = 0, axStat = 0;
epicsUInt32 axErr = 0, highLim = 0, lowLim= 0;
char message[132], *axMessage;
@ -744,6 +770,9 @@ asynStatus SeleneAxis::setPosition(double position)
"Setting Position on %d to value %f, command: %s",
axisNo_, position/MULT, command);
next_poll = -1;
return status;
}
/*======================================= Selene Lift Axis Code ===========================================*/
@ -776,6 +805,8 @@ asynStatus LiftAxis::move(double position, int relative, double min_velocity,
status = pC_->lowLevelWriteRead(axisNo_,command, response);
waitStart = 1;
next_poll = -1;
return status;
}
/*--------------------------------------------------------------------------------------------------------
@ -786,6 +817,11 @@ asynStatus LiftAxis::poll(bool *moving)
{
asynStatus status;
// Protect against excessive polling
if(time(NULL) < next_poll){
return asynSuccess;
}
status = getAxisStatus(moving);
if(*moving == false && waitStart == 1){
*moving = true;
@ -795,6 +831,12 @@ asynStatus LiftAxis::poll(bool *moving)
if(*moving){
waitStart = 0;
}
if(*moving){
next_poll = time(NULL) + BUSYPOLL;
} else {
next_poll = time(NULL) + IDLEPOLL;
}
callParamCallbacks();
return status;
}

View File

@ -28,7 +28,7 @@ class pmacAxis : public SINQAxis
{
public:
/* These are the methods we override from the base class */
pmacAxis(pmacController *pController, int axisNo);
pmacAxis(pmacController *pController, int axisNo, bool enable=true);
virtual ~pmacAxis();
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration);
asynStatus moveVelocity(double min_velocity, double max_velocity, double acceleration);
@ -62,6 +62,10 @@ class pmacAxis : public SINQAxis
int homing;
double statusPos;
time_t next_poll;
bool autoEnable;
friend class pmacController;
};
/*----------------------------------------------------------------------------------------------*/
@ -83,12 +87,11 @@ class pmacHRPTAxis : public pmacAxis
/*--------------------------------------------------------------------------------------------*/
class SeleneAxis : public pmacAxis
{
public:
SeleneAxis(SeleneController *pController, int axisNo, double limitTarget);
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration);
asynStatus setPosition(double position);
asynStatus home(double min_velocity, double max_velocity, double acceleration, int forwards);
public:
SeleneAxis(SeleneController *pController, int axisNo, double limitTarget);
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration);
asynStatus setPosition(double position);
asynStatus home(double min_velocity, double max_velocity, double acceleration, int forwards);
protected:
friend class SeleneController;
@ -112,6 +115,10 @@ class SeleneAxis : public pmacAxis
Mark Koennecke, February 2020
The axis should not be enabled automatically
Michele Brambilla, February 2020
*/
class LiftAxis : public pmacAxis
{

View File

@ -140,8 +140,8 @@ extern "C" {
}
pmacController::pmacController(const char *portName, const char *lowLevelPortName, int lowLevelPortAddress,
int numAxes, double movingPollPeriod, double idlePollPeriod)
: SINQController(portName, lowLevelPortName, numAxes+1)
int numAxes, double movingPollPeriod, double idlePollPeriod, const int& extraParams)
: SINQController(portName, lowLevelPortName, numAxes+1, extraParams)
{
static const char *functionName = "pmacController::pmacController";
@ -544,7 +544,20 @@ asynStatus SeleneCreateController(const char *portName, const char *lowLevelPort
return asynSuccess;
}
/*---------------------------------------------------------------------------------------------------------------------*/
SeleneController::SeleneController(const char *portName, const char *lowLevelPortName, int lowLevelPortAddress,
int numAxes, double movingPollPeriod, double idlePollPeriod) : pmacController(portName,
lowLevelPortName,
lowLevelPortAddress,
numAxes,
movingPollPeriod,
idlePollPeriod, 3) {
static const char *functionName = "seleneController::seleneController";
createParam(MotorSetPositionString, asynParamFloat64, &setMotorPosition_);
callParamCallbacks();
}
/**
* C wrapper for the pmacAxis constructor.
* See pmacAxis::pmacAxis.
@ -711,9 +724,9 @@ asynStatus SeleneController::writeFloat64(asynUser *pasynUser, epicsFloat64 valu
/* Set the parameter and readback in the parameter library. */
status = pAxis->setDoubleParam(function, value);
// TODO: somethign is really shitty here: lowLimit and highLimit cannot be on the command
if (function == motorLowLimit_) {
sprintf(command, "Q%d54=%f", pAxis->axisNo_, (float)value/(float)MULT);
// sprintf(command, "Q%d54=%d", pAxis->axisNo_, (int)(value/pAxis->scale_/MULT));
asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
"%s: Setting low limit on controller %s, axis %d to %f\n",
functionName, portName, pAxis->axisNo_, value);
@ -724,7 +737,13 @@ asynStatus SeleneController::writeFloat64(asynUser *pasynUser, epicsFloat64 valu
"%s: Setting high limit on controller %s, axis %d to %f\n",
functionName, portName, pAxis->axisNo_, value);
errlogPrintf("Setting high limit of axis %d to %f, command = %s\n", pAxis->axisNo_, value, command);
}
} else if(function == setMotorPosition_){
snprintf(command,sizeof(command),"Q%d59=%f", pAxis->axisNo_, value);
asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
"%s: Defining position of axis%d to %f\n",
functionName, pAxis->axisNo_, value);
errlogPrintf("Defining position of axis %d to %f, command = %s\n", pAxis->axisNo_, value, command);
}
//Execute the command.
if (command[0] != 0 && status == asynSuccess) {

View File

@ -26,8 +26,8 @@
class pmacController : public SINQController {
public:
pmacController(const char *portName, const char *lowLevelPortName, int lowLevelPortAddress, int numAxes, double movingPollPeriod,
double idlePollPeriod);
pmacController(const char *portName, const char *lowLevelPortName, int lowLevelPortAddress, int numAxes, double movingPollPeriod,
double idlePollPeriod, const int& extraParams=2);
virtual ~pmacController();
@ -143,22 +143,24 @@ class pmacController : public SINQController {
#define NUM_PMAC_PARAMS (&LAST_PMAC_PARAM - &FIRST_PMAC_PARAM + 1)
#define MotorSetPositionString "SET_MOTOR_POSITION"
class SeleneController : public pmacController {
public:
SeleneController(const char *portName, const char *lowLevelPortName, int lowLevelPortAddress,
int numAxes, double movingPollPeriod, double idlePollPeriod) : pmacController(portName,
lowLevelPortName,
lowLevelPortAddress,
numAxes,
movingPollPeriod,
idlePollPeriod) {};
SeleneController(const char *portName, const char *lowLevelPortName, int lowLevelPortAddress,
int numAxes, double movingPollPeriod, double idlePollPeriod);
~SeleneController(void) { }
// overloaded because we have a different command to set the limits
asynStatus writeFloat64(asynUser *pasynUser, epicsFloat64 value);
friend class SeleneAxis;
friend class pmacAxis;
protected:
int setMotorPosition_;
};

88
testEuroMoveUsb.py Executable file
View File

@ -0,0 +1,88 @@
#!/usr/bin/env python3
# -*- coding: iso-8859-1 -*-
"""
Created on Thur Feb 06 10:22:42 2020
@author: gaston emmanuel exil
"""
#pip install pyusb
#sudo cp 10-llbDevices.rules /etc/udev/rules.d/
#sudo udevadm control --reload-rules && udevadm trigger
#reboot
import sys
import usb.core
import usb.util
from time import sleep
# 1. Device
llbVendorID=0x04B4
llbProductID=0x1002
# 2. Configuration
CONFIGURATION_EV3 = 1 # 1-based
# 3. Interface
INTERFACE_EV3 = 0 # 0-based
# 4. Alternate setting
SETTING_EV3 = 0 # 0-based
# 5. Endpoint
ENDPOINT_EV3 = 1 # 0-based
# find our device
device = usb.core.find(idVendor=llbVendorID, idProduct=llbProductID)
# was it found?
if device is None:
raise ValueError('Device not found')
sys.exit(1)
else:
if device._manufacturer is None:
device._manufacturer = usb.util.get_string(device, device.iManufacturer)
print("manufacturer: ", str(device._manufacturer))
if device._product is None:
device._product = usb.util.get_string(device, device.iProduct)
print("product: ", str(device._product))
# By default, the kernel will claim the device and make it available via
# /dev/usb/hiddevN and /dev/hidrawN which also prevents us
# from communicating otherwise. This removes these kernel devices.
# Yes, it is weird to specify an interface before we get to a configuration.
if device.is_kernel_driver_active(INTERFACE_EV3):
print("Detaching kernel driver")
device.detach_kernel_driver(INTERFACE_EV3)
# claim the device
usb.util.claim_interface(device, INTERFACE_EV3)
# write endpoint
endpoint_BulkOut = device[0][(0,0)][0]
# Read endpoint
endpoint_BulkIn = device[0][(0,0)][2]
try:
command = "L"+chr(13)
assert device.write(endpoint_BulkOut, command.encode('utf-8'), 100) == len(command)
ret = device.read(endpoint_BulkIn, endpoint_BulkIn.wMaxPacketSize, timeout=30000)
print(ret)
#byte_str = "".join(chr(n) for n in ret)
print(ret.tostring())
command = "A1,4"+chr(13)
assert device.write(endpoint_BulkOut, command.encode('utf-8'), 100) == len(command)
ret = device.read(endpoint_BulkIn, endpoint_BulkIn.wMaxPacketSize, timeout=30000)
print(ret)
byte_str = "".join(chr(n) for n in ret)
print(ret.tostring())
except Exception as e:
print(e)
# release the device
usb.util.release_interface(device, INTERFACE_EV3)
# reattach the device to the OS kernel
#device.attach_kernel_driver(INTERFACE_EV3)