diff --git a/.gitignore b/.gitignore index baae805..b205e48 100644 --- a/.gitignore +++ b/.gitignore @@ -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 +._* diff --git a/10-llbDevices.rules b/10-llbDevices.rules new file mode 100644 index 0000000..c939cfd --- /dev/null +++ b/10-llbDevices.rules @@ -0,0 +1 @@ +SUBSYSTEM=="usb", ATTR{idVendor}=="04b4", ATTR{idProduct}=="1002", MODE="0666" GROUP="plugdev", TAG+="uaccess" diff --git a/Makefile.RHEL7 b/Makefile.RHEL7 index bdd2198..9875c20 100644 --- a/Makefile.RHEL7 +++ b/Makefile.RHEL7 @@ -2,7 +2,7 @@ include /ioc/tools/driver.makefile MODULE=sinq - +LIBVERSION=koennecke BUILDCLASSES=Linux # additional module dependencies diff --git a/sinqEPICSApp/Db/motorSet.db b/sinqEPICSApp/Db/motorSet.db new file mode 100644 index 0000000..91d7b80 --- /dev/null +++ b/sinqEPICSApp/Db/motorSet.db @@ -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") +} diff --git a/sinqEPICSApp/src/SINQController.cpp b/sinqEPICSApp/src/SINQController.cpp index afbe3ef..4070156 100644 --- a/sinqEPICSApp/src/SINQController.cpp +++ b/sinqEPICSApp/src/SINQController.cpp @@ -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, diff --git a/sinqEPICSApp/src/SINQController.h b/sinqEPICSApp/src/SINQController.h index 15850e3..27cdd62 100644 --- a/sinqEPICSApp/src/SINQController.h +++ b/sinqEPICSApp/src/SINQController.h @@ -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: diff --git a/sinqEPICSApp/src/pmacAxis.cpp b/sinqEPICSApp/src/pmacAxis.cpp index 1dd82a2..e9f4aa2 100644 --- a/sinqEPICSApp/src/pmacAxis.cpp +++ b/sinqEPICSApp/src/pmacAxis.cpp @@ -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; } diff --git a/sinqEPICSApp/src/pmacAxis.h b/sinqEPICSApp/src/pmacAxis.h index 9aea827..3577581 100644 --- a/sinqEPICSApp/src/pmacAxis.h +++ b/sinqEPICSApp/src/pmacAxis.h @@ -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 { diff --git a/sinqEPICSApp/src/pmacController.cpp b/sinqEPICSApp/src/pmacController.cpp index d444514..55deb11 100644 --- a/sinqEPICSApp/src/pmacController.cpp +++ b/sinqEPICSApp/src/pmacController.cpp @@ -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) { diff --git a/sinqEPICSApp/src/pmacController.h b/sinqEPICSApp/src/pmacController.h index 8f06c88..2f67fcf 100644 --- a/sinqEPICSApp/src/pmacController.h +++ b/sinqEPICSApp/src/pmacController.h @@ -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_; + }; diff --git a/testEuroMoveUsb.py b/testEuroMoveUsb.py new file mode 100755 index 0000000..8fd6989 --- /dev/null +++ b/testEuroMoveUsb.py @@ -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)