47 Commits

Author SHA1 Message Date
0cbfe45893 Fixed ErormoveDriver to stop only on end switches when the target
exceeds the limits. Otherwise motors would get stuck on the switches
when trying to move off them.
2022-08-16 10:54:10 +02:00
bb1c10c7cd EL737 Increase timeout
The EL737 module micht give rise to a failure if the counterbox
does not reply withing 1s. This is the case at FOCUS.
With this commit the timeout is increased to 2s.
2022-07-21 13:42:31 +02:00
339da2e9ff PMAC V3: axis status 5,6 not a failure
The old implementation assumes that the axis staying in state 5,6
for too much time a implies a failure. This is no longer the case.
2022-07-21 13:37:32 +02:00
80877aa6ab pmacV3 read speed and evaluate ETA 2022-05-27 15:21:46 +02:00
afc92bde3f Improved status reporting 2022-05-20 10:38:32 +02:00
8e2c1af10e Some cleanup, force reload encoder before enable 2022-05-20 08:35:42 +00:00
f5da0d54bd Reduce polling, se MsgTxt if move when disabled 2022-05-20 08:35:42 +00:00
ad05433602 Bugfix + cleanup 2022-05-20 08:35:42 +00:00
92364a1de8 Can set and read enable, something broken with movements and status 2022-05-20 08:35:42 +00:00
403eecafae pmac Enable PV successfully tested 2022-05-20 08:35:42 +00:00
fbf2331a05 pmacV3 read axis state 2022-05-20 08:35:42 +00:00
14bbda3364 Add pmacV3 poll 2022-05-20 08:35:42 +00:00
c7fea08718 pmacV3 enable/disable axis
The default behaviour of the pmac controller is to enable the motor at startup. With the controller V3 we want that at IOC startup the axis to be off.
A database entry like

record(longout, "$(P)$(M):Enable") {
    field(DTYP, "asynInt32")
    field(OUT, "@asyn($(PORT),$(N),1) ENABLE_AXIS")
    field(PINI, "YES")
}

can be used to give access to enable/disable the axis.
2022-05-20 08:35:42 +00:00
edc71af235 Bugfix pmacV3 2022-05-20 08:35:42 +00:00
c805385ad1 Allow enable/disable (tentative) 2022-05-20 08:35:42 +00:00
929f9f600d Add PMAC V3
- limit switches: use PXX37, PXX38 insteam of MXX21, MXX22
- if the motor is running query for axis position (QXX10) and status (PXX00)
- else query also error status (PXX01) and limit switches (PXX37-8)
- make the requests in 1+1 transactions
2022-05-20 08:35:42 +00:00
8c3e68394f Changes to Scaler, add build for version 7 in Makefile 2022-05-20 10:25:41 +02:00
646607e476 EuroMove fix "addrCommand" bug in controller 2022-03-02 06:41:39 +01:00
68a265b199 EL734 positioning failure + improve logging 2022-03-02 06:39:18 +01:00
542abcbaad pmac improve handling of the error message 2022-03-02 06:37:33 +01:00
efbd0e19cf Updated EuromoveDriver to work with Prologix GPIB-Ethernet
Improved threshold handling on EL737
Minor changes
2021-06-17 09:30:28 +02:00
6de4a878ef Merge branch 'amor-selene'
Conflicts:
	.gitignore
	sinqEPICSApp/src/pmacAxis.cpp
	sinqEPICSApp/src/pmacAxis.h
	sinqEPICSApp/src/pmacController.cpp
	sinqEPICSApp/src/pmacController.h
2020-10-01 09:31:23 +02:00
d86da602d2 Added .gitignore 2020-07-09 14:19:13 +02:00
c9e7830274 - Fixed reference run for Phytron
- Added brake handling to phytron
- Added code to set speed for phytron
- Removed scaling on Selene pmac limits
- Removed enabling on Selene pmac axis
2020-07-09 14:15:26 +02:00
d1d74f4db3 Merge branch 'master' of ssh://git.psi.ch/sinqdev/sinqepicsapp 2020-06-23 09:20:17 +02:00
d1beb9b0d8 Merge branch 'master' of ssh://git.psi.ch/sinqdev/sinqepicsapp into amor-selene 2020-06-23 09:19:25 +02:00
3ae13875cf Added fucking brake support to Phytron 2020-06-23 09:18:12 +02:00
f56ef2c74c Committing the last status of selene work in the corona lockdown.
The problem is in the MCU, probably
2020-03-18 15:53:44 +01:00
a25f8cabb9 Selene positions do not need factor *1000 2020-02-19 15:39:23 +01:00
c4fe45c0cb Add a set PV for the SeleneMotor
- make the number of extra parameters in SINQController configurable
    - add database entry for the motor set field
    - add the new function "setMotorPosition_" to the Selene controller
    - execute Qx59=<pos> to set the new position
2020-02-18 12:32:24 +01:00
3b7133ecfe Make sure that the SINQ library version stays the same 2020-02-18 12:31:25 +01:00
e9a615d0fa Changes in pmac controller 2020-02-17 11:10:02 +01:00
12249d5471 Implemented Homing on the EuroMove driver 2020-02-10 10:00:56 +01:00
6d823e2265 Removed a debug statement from EuroMoveDriver.cpp 2020-01-24 13:04:40 +01:00
75de7a3a4c First version of a driver for the LLB EuroMove controller. USB feature still missing. 2020-01-21 14:17:17 +01:00
5aefbd4684 Fixed a bug in the pmac driver which caused wrong state 5/6 errors to be
found on a slow motor.
2019-10-30 11:32:33 +01:00
3133d933fa Minor fixes to the PhytronDriver
Removed some annoying messages grom Nanotec Driver
2019-10-10 11:35:25 +02:00
bf2ff63a4b Fixed bugs in EL734
Phytron motor driver is now working
May be fixed inverted limits on pmac
2019-05-23 16:50:10 +02:00
b4b093efdf Some cleanup in EL734Driver.cpp 2019-05-20 09:17:33 +02:00
f424477a6a Fixed a bug with inproperly cleared MsgTxt in SINQAxis
Extended pmacAxis to properly deal with HRPT pmac motors which have an extra error condition
2019-05-13 12:03:24 +02:00
3bea34700e Added MsgTxt support to Nanotec motor driver 2019-01-08 11:55:50 +01:00
02ab5ff9b8 Added MsgTxt support to pmac controller driver 2019-01-08 11:47:02 +01:00
972131d86a Added new system disconnect reporting to the phytron driver 2019-01-08 11:35:09 +01:00
f909c41a2b Updated the Phytron driver to use MsgTxt
Updated the Phytron driver to use a selector in order to support the use case of multiple phytrons on one channel
The chnages still need to be tested
2019-01-08 09:41:34 +01:00
460030e410 Made EL737 send an empty string in MsgTxt on success
Fixed a type in an error message
2018-11-07 16:26:58 +01:00
2a964503a4 Added a number of auxiliary Makefiles
- MakeScaler.RHEL7 to build the EPICS scaler module from source. We use a better one then the standard one.
- MakeAutoSave.RHEL7 to build  autosave from the PSI module system
- Modified the EL734Driver to set the MsgTxt to NULL when everything is happy
2018-11-07 16:18:16 +01:00
1687ebf4c7 Added a README to sinqEPICSApp 2017-11-08 09:58:18 +01:00
40 changed files with 3342 additions and 578 deletions

121
.gitignore vendored Normal file
View File

@ -0,0 +1,121 @@
# 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"

22
MakeAutoSave.RHEL7 Normal file
View File

@ -0,0 +1,22 @@
# This Makefile has to be executed in the synApps-xxx/support/autosave-xxx directory.
# It creates a autosave module to load into iocsh
include /ioc/tools/driver.makefile
MODULE=autosave
BUILDCLASSES=Linux
USR_DEPENDENCIES=
SOURCES += asApp/src/dbrestore.c
SOURCES += asApp/src/save_restore.c
SOURCES += asApp/src/initHooks.c
SOURCES += asApp/src/fGetDateStr.c
SOURCES += asApp/src/configMenuSub.c
SOURCES += asApp/src/verify.c
SOURCES += asApp/src/asVerify.c
SOURCES += asApp/src/os/Linux/osdNfs.c
HEADERS += asApp/src/os/Linux/osdNfs.h
DBDS += asApp/src/asSupport.dbd

17
MakeScaler.RHEL7 Normal file
View File

@ -0,0 +1,17 @@
include /ioc/tools/driver.makefile
MODULE=scaler
BUILDCLASSES=Linux
USR_DEPENDENCIES = asyn,427.0.1
SOURCES += stdApp/src/scalerRecord.c
SOURCES += stdApp/src/devScalerAsyn.c
SOURCES += stdApp/src/drvScalerSoft.c
HEADERS += devScaler.h
DBDS += stdApp/src/scalerRecord.dbd
DBDS += stdApp/src/scalerSupport.dbd

View File

@ -2,12 +2,14 @@
include /ioc/tools/driver.makefile
MODULE=sinq
LIBVERSION=koennecke
BUILDCLASSES=Linux
EPICS_VERSIONS=3.14.12 7.0.4.1
# additional module dependencies
REQUIRED+=SynApps
REQUIRED+=stream
REQUIRED+=scaler
# using a test version
scaler_VERSION=koennecke
@ -26,6 +28,7 @@ SOURCES += sinqEPICSApp/src/EL734Driver.cpp
SOURCES += sinqEPICSApp/src/NanotecDriver.cpp
SOURCES += sinqEPICSApp/src/stptok.cpp
SOURCES += sinqEPICSApp/src/PhytronDriver.cpp
SOURCES += sinqEPICSApp/src/EuroMoveDriver.cpp
SOURCES += sinqEPICSApp/src/pmacAsynIPPort.c
SOURCES += sinqEPICSApp/src/pmacAxis.cpp
SOURCES += sinqEPICSApp/src/pmacController.cpp

37
Makefile.RHEL7.michele Normal file
View File

@ -0,0 +1,37 @@
# This build the sinq extensions for the PSI EPICS setup
include /ioc/tools/driver.makefile
MODULE=sinq
LIBVERSION=brambilla_m
BUILDCLASSES=Linux
EPICS_VERSIONS=3.14.12 7.0.4.1
ARCH_FILTER=RHEL7-x86_64
# additional module dependencies
REQUIRED+=SynApps
REQUIRED+=stream
REQUIRED+=scaler
# using a test version
scaler_VERSION=koennecke
TEMPLATES += sinqEPICSApp/Db/dimetix.db
TEMPLATES += sinqEPICSApp/Db/slsvme.db
TEMPLATES += sinqEPICSApp/Db/spsamor.db
DBDS += sinqEPICSApp/src/sinq.dbd
# What we need at SINQ
SOURCES += sinqEPICSApp/src/devScalerEL737.c
SOURCES += sinqEPICSApp/src/SINQController.cpp
SOURCES += sinqEPICSApp/src/SINQAxis.cpp
SOURCES += sinqEPICSApp/src/EL734Driver.cpp
SOURCES += sinqEPICSApp/src/NanotecDriver.cpp
SOURCES += sinqEPICSApp/src/stptok.cpp
SOURCES += sinqEPICSApp/src/PhytronDriver.cpp
SOURCES += sinqEPICSApp/src/EuroMoveDriver.cpp
SOURCES += sinqEPICSApp/src/pmacAsynIPPort.c
SOURCES += sinqEPICSApp/src/pmacAxis.cpp
SOURCES += sinqEPICSApp/src/pmacController.cpp
# MISCS would be the place to keep the stream device template files

40
README.md Normal file
View File

@ -0,0 +1,40 @@
# SinqEPICSApp
This is an IOC application which contains the drivers for SINQ hardware.
EPICS purists may find it messy because it contains the drivers for all
SINQ stuff in one package rather then in separate modules. This is just
for ease of distribution. The idea being that all of SINQ shares one
module/IOC application and configures in its startup file what it needs
of it.
Drivers included:
- EL734 motor controller
- SINQ Delta Tau PMAC motor controller. This is a modified version of the Diamond
PMAC code. The modification is necessary because PSI runs a different program in
the MCU at SINQ then Diamond
- Nanotec bus motor driver
- Phytron MCU200 motor driver
- EL737 counter box driver through the scaler record
There are some streamdevice protocol files provided:
- spss5: for the Siemens SPS5 with the PSI custom RS232 module
- slsvme: for the SLS magnet controllers managed by the SINQ special vmemaggi program
- el755: the old SINQ magnet controller
- dimetix: for the deimetix distance measurement device at AMOR
There are two main branches here:
- Of course master from which you can build the PSI RHEL7 module and the EEE module
- The amorsim branch in which you can build the IOC application for ESS against the ICS
setup. This is needed for the sinq-amorsim simulation. This branch exists because of the
political problem to get a module into the ICS EEE setup.
Those political problems require a special development model:
1. If you do something, do it in a separate branch
2. When reasonably sure that what you did works, merge it both into master and amorsim
Take care of the sinqEPICsApp/src/sinq.dbd file. This is the one which differs mostly between
amorsim and master branches.

View File

@ -25,17 +25,20 @@ TEMPLATE_TOP=$(EPICS_BASE)/templates/makeBaseApp/top
#SNCSEQ=$(EPICS_BASE)/../modules/soft/seq
# EPICS_BASE usually appears last so other apps can override stuff:
EPICS_BASE=/opt/epics/bases/base-3.14.12.5
#EPICS_BASE=
#MODULES=/home/epics/modules
# Set RULES here if you want to take build rules from somewhere
# other than EPICS_BASE:
#RULES=/path/to/epics/support/module/rules/x-y
MOTOR=/opt/epics/modules/motor/6.10.0/3.14.12.5
ASYN=/opt/epics/modules/asyn/4.27.0/3.14.12.5
SYNAPPSSTD=/opt/epics/modules/synAppsStd/3.4.1/3.14.12.5/
#ANC=/usr/local/epics/anc350v17
STREAMS=/opt/epics/modules/streamdevice/2.6.0/3.14.12.5
#LAKESHORE336=/usr/local/epics/support/lakeshore336
BUSY=/opt/epics/modules/busy/1.6.0/3.14.12.5
#OXINSTCRYOJET=/usr/local/epics/support/OxInstCryojet-2-18-3
PCRE=/opt/epics/modules/pcre/8.36.0/3.14.12.5
##RULES=/path/to/epics/support/module/rules/x-y
#MOTOR=/opt/epics/modules/motor/6.10.0/3.14.12.5
#ASYN=/opt/epics/modules/asyn/4.27.0/3.14.12.5
#SYNAPPSSTD=/opt/epics/modules/synAppsStd/3.4.1/3.14.12.5/
##ANC=/usr/local/epics/anc350v17
#STREAMS=/opt/epics/modules/streamdevice/2.6.0/3.14.12.5
##LAKESHORE336=/usr/local/epics/support/lakeshore336
#BUSY=/opt/epics/modules/busy/1.6.0/3.14.12.5
##OXINSTCRYOJET=/usr/local/epics/support/OxInstCryojet-2-18-3
#PCRE=/opt/epics/modules/pcre/8.36.0/3.14.12.5

View File

@ -0,0 +1,23 @@
#!/usr/local/bin/iocsh
require sinq,koennecke
epicsEnvSet("TOP","/afs/psi.ch/project/sinqdev/sinqepicsapp/iocBoot/iocsinqEPICS")
epicsEnvSet("BASE","/afs/psi.ch/project/sinqdev/sinqepicsapp")
cd ${TOP}
drvAsynIPPortConfigure("serial1", "129.129.195.83:1234",0,0,0)
#drvAsynIPPortConfigure("serial1", "amor-ts:3002",0,0,0)
#drvAsynIPPortConfigure("serial1", "localhost:9090",0,0,0)
EuroMoveCreateController("llb","serial1", 10, 1);
### Motors
dbLoadRecords("$(BASE)/sinqEPICSApp/Db/asynRecord.db","P=KM36:,R=serial1,PORT=serial1,ADDR=0,OMAX=80,IMAX=80")
dbLoadTemplate "motor.substitutions.eurollb"
iocInit

View File

@ -1,4 +1,4 @@
file "$(MOTOR)/db/basic_asyn_motor.db"
qfile "$(MOTOR)/db/basic_asyn_motor.db"
{
pattern
{P, N, M, DTYP, PORT, ADDR, DESC, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, MRES, PREC, DHLM, DLLM, INIT}

View File

@ -0,0 +1,13 @@
file "$(BASE)/sinqEPICSApp/Db/basic_asyn_motor.db"
{
pattern
{P, N, M, DTYP, PORT, ADDR, DESC, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, MRES, PREC, RDBD, DHLM, DLLM, INIT}
{KM36:llb:, 1, "m$(N)", "asynMotor", llb, 1, "m1", mm, Pos, 2.0, 0.1, .2, 0, 1, .2, .01, 3, 0.2, 1000, 0, "1"}
}
file "$(BASE)/sinqEPICSApp/Db/motorMessage.db"
{
pattern
{P,N, M,PORT}
{KM36:llb:, 1, "m$(N)",llb}
}

View File

@ -1,7 +1,15 @@
file "$(MOTOR)/db/basic_asyn_motor.db"
file "$(BASE)/sinqEPICSApp/Db/basic_asyn_motor.db"
{
pattern
{P, N, M, DTYP, PORT, ADDR, DESC, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, MRES, PREC, DHLM, DLLM, INIT}
{KM36:phytron:, 1, "m$(N)", "asynMotor", phy, 1, "m1", mm, Pos, 2.0, 0.1, .2, 0, 1, .2, .001, 3, 100, -100, "1"}
{KM36:phytron:, 2, "m$(N)", "asynMotor", phy, 2, "m2", mm, Pos, 2.0, 0.1, .2, 0, 1, .2, .001, 3, 100, -100, "10"}
{P, N, M, DTYP, PORT, ADDR, DESC, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, MRES, PREC, RDBD, DHLM, DLLM, INIT}
{KM36:phytron:, 1, "m$(N)", "asynMotor", phy, 1, "m1", mm, Pos, 2.0, 0.1, .2, 0, 1, .2, .001, 3, 0.2, 100, -100, "1"}
{KM36:phytron:, 2, "m$(N)", "asynMotor", phy, 2, "m2", mm, Pos, 2.0, 0.1, .2, 0, 1, .2, .001, 3, 0.2, 100, -100, "10"}
}
file "$(BASE)/sinqEPICSApp/Db/motorMessage.db"
{
pattern
{P,N, M,PORT}
{KM36:phytron:, 1, "m$(N)",phy}
{KM36:phytron:, 2, "m$(N)",phy}
}

View File

@ -0,0 +1,25 @@
#!/usr/local/bin/iocsh
require sinq,koennecke
epicsEnvSet("TOP","/afs/psi.ch/project/sinqdev/sinqepicsapp/iocBoot/iocsinqEPICS")
epicsEnvSet("BASE","/afs/psi.ch/project/sinqdev/sinqepicsapp")
epicsEnvSet("dbPATH","${EPICS_BASE}/dbd:${ASYN}/dbd:${MOTOR}/dbd")
cd ${TOP}
## Register all support components
dbLoadDatabase "../../dbd/sinqEPICS.dbd"
drvAsynIPPortConfigure("serial1", "129.129.195.58:22222",0,0,0)
#drvAsynIPPortConfigure("serial1", "localhost:9090",0,0,0)
PhytronCreateController("phy","serial1","0",1,0);
### Motors
dbLoadRecords("$(BASE)/sinqEPICSApp/Db/asynRecord.db","P=KM36:,R=serial1,PORT=serial1,ADDR=0,OMAX=80,IMAX=80")
dbLoadTemplate "motor.substitutions.phytron"
iocInit

View File

@ -18,19 +18,16 @@ epicsEnvSet("dbPATH","${EPICS_BASE}/dbd::${ASYN}/dbd:${MOTOR}/dbd:${ANC}/dbd")
cd ${TOP}
## Register all support components
dbLoadDatabase "../../dbd/sinqEPICS.dbd"
#dbLoadDatabase "dbd/sinq.dbd"
dbLoadDatabase "../../sinqEPICSApp/src/sinq.dbd"
sinqEPICS_registerRecordDeviceDriver pdbbase
## Load record instances
#dbLoadRecords("db/xxx.db","user=koenneckeHost")
#---------- load Nanotec motor controller
#drvAsynIPPortConfigure("serial1", "narziss-ts:3002",0,0,0)
#drvAsynIPPortConfigure("serial1", "localhost:5050",0,0,0)
drvAsynIPPortConfigure("serial1", "localhost:8080",0,0,0)
PhytronCreateController("phy","serial1",1,1);
PhytronCreateController("phy","serial1","0",1,1);
### Motors

View File

@ -0,0 +1,94 @@
# Database definition for the SLS VME magnets as installed at AMOR
##
## Read the High Limit
##
record(ai, "$(PREFIX)HighLim") {
field(DTYP, "stream")
field(DESC, "High Current Limit")
field(INP, "@slsvme.proto read($(NO),hl) slsvme 0")
field(SCAN, "1 second")
field(PREC, "3")
field(EGU, "A")
}
##
## Read the High Limit
##
record(ai, "$(PREFIX)LowLim") {
field(DTYP, "stream")
field(DESC, "Low Current Limit")
field(INP, "@slsvme.proto read($(NO),ll) slsvme 0")
field(SCAN, "1 second")
field(PREC, "3")
field(EGU, "A")
}
##
## Read the Error code
##
record(ai, "$(PREFIX)ErrCode") {
field(DTYP, "stream")
field(DESC, "Error Code")
field(INP, "@slsvme.proto read($(NO),err) slsvme 0")
field(SCAN, "1 second")
field(PREC, "3")
field(EGU, "A")
}
##
## Read the textual representation of the error.
##
record(stringin, "$(PREFIX)ErrText") {
field(DTYP, "stream")
field(INP, "@slsvme.proto readErrTxt($(NO)) slsvme 0")
field(SCAN, "1 second")
field(PINI, "YES")
}
##
## Read the Current
##
record(ai, "$(PREFIX)CurRBV") {
field(DTYP, "stream")
field(DESC, "Low Current Limit")
field(INP, "@slsvme.proto read($(NO),cur) slsvme 0")
field(SCAN, "1 second")
field(PREC, "3")
field(EGU, "A")
}
##
## Set the current
##
record(ao, "$(PREFIX)CurSet") {
field(DTYP, "stream")
field(DESC, "Setpoint current")
field(OUT, "@slsvme.proto write($(NO),cur) slsvme 0")
field(PREC, "3")
field(EGU, "A")
}
##
## Read power status of the magnet
##
record(bi, "$(PREFIX)PowerStatusRBV") {
field(DESC, "Readback of the power status")
field(DTYP, "stream")
field(INP, "@slsvme.proto readonoff($(NO)) slsvme 0")
field(SCAN, "1 second")
field(ZNAM, "off")
field(ONAM, "on")
}
##
## Set the power status
##
record(bo, "$(PREFIX)PowerStatus") {
field(DESC, "Set the power status")
field(DTYP, "stream")
field(OUT, "@slsvme.proto setpower($(NO)) slsvme 0")
field(ZNAM, "on")
field(ONAM, "off")
}

View File

@ -4,4 +4,4 @@ set dir [pwd]
cd /usr/local/ioc
exec /usr/bin/tar czvf $dir/sinqepics.tgz modules/scaler modules/sinq modules/anc350
exec /usr/bin/tar czvf $dir/sinqepics.tgz modules/scaler modules/sinq modules/anc350 modules/autosave

View File

@ -0,0 +1,9 @@
record(asyn,"$(P)$(R)")
{
field(DTYP,"asynRecordDevice")
field(PORT,"$(PORT)")
field(ADDR,"$(ADDR)")
field(OMAX,"$(OMAX)")
field(IMAX,"$(IMAX)")
}

View File

@ -0,0 +1,23 @@
record(motor,"$(P)$(M)")
{
field(DESC,"$(DESC)")
field(DTYP,"$(DTYP)")
field(DIR,"$(DIR)")
field(VELO,"$(VELO)")
field(VBAS,"$(VBAS)")
field(ACCL,"$(ACCL)")
field(BDST,"$(BDST)")
field(BVEL,"$(BVEL)")
field(BACC,"$(BACC)")
field(OUT,"@asyn($(PORT),$(ADDR))")
field(MRES,"$(MRES)")
field(PREC,"$(PREC)")
field(EGU,"$(EGU)")
field(DHLM,"$(DHLM)")
field(DLLM,"$(DLLM)")
field(INIT,"$(INIT)")
field(TWV,"1")
field(RDBD,"$(RDBD)")
field(RTRY,"0")
}

View File

@ -0,0 +1,21 @@
grecord(motor,"$(P)$(M)")
{
field(DESC,"$(DESC)")
field(DTYP,"$(DTYP)")
field(DIR,"$(DIR)")
field(VELO,"$(VELO)")
field(VBAS,"$(VBAS)")
field(ACCL,"$(ACCL)")
field(BDST,"$(BDST)")
field(BVEL,"$(BVEL)")
field(BACC,"$(BACC)")
field(OUT,"#C$(C) S$(S) @")
field(MRES,"$(MRES)")
field(PREC,"$(PREC)")
field(EGU,"$(EGU)")
field(DHLM,"$(DHLM)")
field(DLLM,"$(DLLM)")
field(INIT,"$(INIT)")
field(TWV,"1")
}

View File

@ -6,4 +6,13 @@ record(waveform, "$(P)$(M)-MsgTxt") {
field(FTVL, "CHAR")
field(NELM, "80")
field(SCAN, "I/O Intr")
}
}
record(ao,"$(P)m$(N)-Resolution"){
field(DESC,"m$(N) Resolution")
field(DOL,"$(P)m$(N).MRES CP MS")
field(OMSL,"closed_loop")
field(DTYP,"asynFloat64")
field(OUT,"@asyn($(PORT),$(N))MOTOR_RESOLUTION")
field(PREC,"3")
}

View File

@ -0,0 +1,13 @@
# workaround over set position
record(ao, "$(P)$(M)-SetPosition") {
field(DTYP, "asynFloat64")
field(OUT, "@asyn($(PORT),$(N),1) SET_MOTOR_POSITION")
field(PINI, "YES")
}
# enable axis
record(longout, "$(P)$(M):Enable") {
field(DTYP, "asynInt32")
field(OUT, "@asyn($(PORT),$(N),1) ENABLE_AXIS")
field(PINI, "YES")
}

View File

@ -41,7 +41,6 @@ EL734Controller::EL734Controller(const char *portName, const char *EL734PortName
{
int axis;
asynStatus status;
EL734Axis *pAxis;
static const char *functionName = "EL734Controller::EL734Controller";
@ -58,7 +57,7 @@ EL734Controller::EL734Controller(const char *portName, const char *EL734PortName
switchRemote();
for (axis=0; axis<numAxes; axis++) {
pAxis = new EL734Axis(this, axis+1);
new EL734Axis(this, axis+1);
}
startPoller(1000./1000., IDLEPOLL, 2);
@ -114,21 +113,20 @@ EL734Axis* EL734Controller::getAxis(int axisNo)
void EL734Controller::switchRemote()
{
char command[COMLEN], reply[COMLEN];
int status;
size_t in, out;
int reason;
strcpy(command,"RMT 1");
status = pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
reply,COMLEN, 1.,&out,&in,&reason);
strcpy(command,"ECHO 0");
status = pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
reply,COMLEN, 1.,&out,&in,&reason);
strcpy(command,"RMT 1");
status = pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
reply,COMLEN, 1.,&out,&in,&reason);
strcpy(command,"ECHO 0");
status = pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
reply,COMLEN, 1.,&out,&in,&reason);
}
/**
@ -149,7 +147,7 @@ asynStatus EL734Controller::transactController(int axisNo,char command[COMLEN],
pasynOctetSyncIO->flush(pasynUserController_);
status = pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
reply,COMLEN, 1.,&out,&in,&reason);
reply,COMLEN, 2.,&out,&in,&reason);
if(status != asynSuccess){
if(axis!= NULL){
axis->updateMsgTxtFromDriver("Lost connection to motor controller");
@ -218,6 +216,27 @@ asynStatus EL734Controller::transactController(int axisNo,char command[COMLEN],
EL734Axis::EL734Axis(EL734Controller *pC, int axisNo)
: SINQAxis(pC, axisNo), pC_(pC)
{
char command[COMLEN], reply[COMLEN];
asynStatus status;
int count = 0;
float low, high;
/*
get the hardware limits from the controller
*/
sprintf(command,"H %d",axisNo_);
status = pC_->transactController(axisNo_,command,reply);
if(status == asynSuccess){
count = sscanf(reply,"%f %f",&low, &high);
if(count >= 2){
pC_->setDoubleParam(axisNo_,pC_->motorLowLimit_,low);
pC_->setDoubleParam(axisNo_,pC_->motorHighLimit_,high);
} else {
errlogPrintf("Bad response - %s - requesting limits at axis %d", reply, axisNo_);
}
} else {
errlogPrintf("Failed to read limits at axis %d", axisNo_);
}
}
@ -253,8 +272,12 @@ asynStatus EL734Axis::move(double position, int relative, double minVelocity, do
}
oredMSR = 0;
homing = 0;
errorReported = 0;
errlogPrintf("Starting axis %d with destination %f", axisNo_,position/1000);
sprintf(command, "p %d %.3f", axisNo_, position/1000.);
status = pC_->transactController(axisNo_,command,reply);
setIntegerParam(pC_->motorStatusProblem_, false);
updateMsgTxtFromDriver("");
next_poll = -1;
return status;
}
@ -267,6 +290,10 @@ asynStatus EL734Axis::home(double minVelocity, double maxVelocity, double accele
// status = sendAccelAndVelocity(acceleration, maxVelocity);
setIntegerParam(pC_->motorStatusProblem_, false);
updateMsgTxtFromDriver("");
errorReported = 0;
sprintf(command, "R %d", axisNo_);
homing = 1;
next_poll= -1;
@ -285,6 +312,7 @@ asynStatus EL734Axis::moveVelocity(double minVelocity, double maxVelocity, doubl
// functionName, minVelocity, maxVelocity, acceleration);
errorReported = 0;
if (maxVelocity > 0.) {
/* This is a positive move */
sprintf(command, "FF %d", axisNo_);
@ -293,20 +321,24 @@ asynStatus EL734Axis::moveVelocity(double minVelocity, double maxVelocity, doubl
sprintf(command, "FB %d", axisNo_);
}
status = pC_->transactController(axisNo_,command,reply);
setIntegerParam(pC_->motorStatusProblem_, false);
updateMsgTxtFromDriver("");
next_poll = -1;
return status;
}
asynStatus EL734Axis::stop(double acceleration )
{
asynStatus status;
asynStatus status = asynSuccess;
//static const char *functionName = "EL734Axis::stop";
char command[COMLEN], reply[COMLEN];
sprintf(command, "S %d", axisNo_);
status = pC_->transactController(axisNo_,command,reply);
errlogPrintf("Sent STOP on Axis %d\n", axisNo_);
updateMsgTxtFromDriver("Axis interrupted");
if(errorReported == 0){
sprintf(command, "S %d", axisNo_);
status = pC_->transactController(axisNo_,command,reply);
errlogPrintf("Sent STOP on Axis %d\n", axisNo_);
updateMsgTxtFromDriver("Axis interrupted");
}
return status;
}
@ -317,7 +349,7 @@ asynStatus EL734Axis::setPosition(double position)
//static const char *functionName = "EL734Axis::setPosition";
char command[COMLEN], reply[COMLEN];
sprintf(command, "P %d %f", axisNo_, position/1000.);
sprintf(command, "U %d %f", axisNo_, position/1000.);
status = pC_->transactController(axisNo_,command,reply);
next_poll = -1;
@ -345,10 +377,8 @@ asynStatus EL734Axis::setClosedLoop(bool closedLoop)
asynStatus EL734Axis::poll(bool *moving)
{
int msr, count;
asynStatus comStatus;
asynStatus comStatus = asynSuccess;
char command[COMLEN], reply[COMLEN], errTxt[256];
int driverError = 0;
// protect against excessive polling
if(time(NULL) < next_poll){
@ -357,97 +387,121 @@ asynStatus EL734Axis::poll(bool *moving)
}
// Read the current motor position
setIntegerParam(pC_->motorStatusProblem_,false);
sprintf(command,"u %d", axisNo_);
comStatus = pC_->transactController(axisNo_,command,reply);
if(comStatus == asynError){
driverError = 1;
setIntegerParam(pC_->motorStatusProblem_,true);
goto skip;
}
if(comStatus) goto skip;
if(strstr(reply,"*ES") != NULL){
*moving = false;
setIntegerParam(pC_->motorStatusDone_, true);
setIntegerParam(pC_->motorStatusProblem_, true);
errorReported = 1;
updateMsgTxtFromDriver("Emergency Stop Engaged");
driverError = 1;
comStatus = asynError;
goto skip;
} else if(strstr(reply,"?BSY") != NULL){
*moving = true;
setIntegerParam(pC_->motorStatusDone_, false);
updateMsgTxtFromDriver(NULL);
goto skip;
}
count = sscanf(reply,"%lf", &position);
if(count != 1) {
snprintf(errTxt,sizeof(errTxt),"Bad reply %s when reading position for %d", reply, axisNo_);
updateMsgTxtFromDriver(errTxt);
comStatus = asynError;
driverError =1;
goto skip;
if(!homing) {
snprintf(errTxt,sizeof(errTxt),"Bad reply %s when reading position for %d", reply, axisNo_);
setIntegerParam(pC_->motorStatusProblem_, true);
errorReported = 1;
updateMsgTxtFromDriver(errTxt);
comStatus = asynError;
goto skip;
}
} else {
errlogPrintf("Axis %d, reply %s, position %lf\n", axisNo_, reply, position);
setDoubleParam(pC_->motorPosition_, position*1000);
//setDoubleParam(pC_->motorEncoderPosition_, position);
}
//errlogPrintf("Axis %d, reply %s, position %lf\n", axisNo_, reply, position);
setDoubleParam(pC_->motorPosition_, position*1000);
//setDoubleParam(pC_->motorEncoderPosition_, position);
// Read the moving status of this motor
sprintf(command,"msr %d",axisNo_);
comStatus = pC_->transactController(axisNo_,command,reply);
if(comStatus) goto skip;
if(comStatus == asynError){
setIntegerParam(pC_->motorStatusProblem_,true);
goto skip;
}
sscanf(reply,"%x",&msr);
// errlogPrintf("Axis %d, reply %s, msr %d, position = %lf\n",
// axisNo_, reply, msr, position);
//errlogPrintf("Axis %d, reply %s, msr %d, oredmsr = %d, position = %lf\n",
// axisNo_, reply, msr, oredMSR, position);
oredMSR |= msr;
if( (msr & 0x1) == 0){
// done: check for trouble
//errlogPrintf("Axis %d finished\n", axisNo_);
*moving = false;
setIntegerParam(pC_->motorStatusDone_, true);
next_poll = time(NULL)+IDLEPOLL;
if(oredMSR & 0x10){
setIntegerParam(pC_->motorStatusLowLimit_, true);
updateMsgTxtFromDriver("Lower Limit Hit");
driverError = 1;
errorReported = 1;
comStatus = asynError;
goto skip;
} else {
setIntegerParam(pC_->motorStatusLowLimit_, false);
}
if(oredMSR & 0x20){
setIntegerParam(pC_->motorStatusHighLimit_, true);
updateMsgTxtFromDriver("Upper Limit Hit");
driverError = 1;
errorReported = 1;
comStatus = asynError;
goto skip;
} else {
setIntegerParam(pC_->motorStatusHighLimit_, false);
}
if(homing){
setIntegerParam(pC_->motorStatusAtHome_, true);
}
setIntegerParam(pC_->motorStatusProblem_, false);
if(oredMSR &0x1000){
if(oredMSR & 0x1000){
setIntegerParam(pC_->motorStatusProblem_, true);
// errlogPrintf("Detected air cushion error on %d", axisNo_);
errlogSevPrintf(errlogMajor, "Air cushion problem on %d", axisNo_);
updateMsgTxtFromDriver("Air cushion error");
driverError = 1;
errorReported = 1;
comStatus = asynError;
goto skip;
}
if(oredMSR &0x80){
if(oredMSR & 0x100){
setIntegerParam(pC_->motorStatusProblem_, true);
errlogSevPrintf(errlogMajor, "Positioning fault at %d", axisNo_);
updateMsgTxtFromDriver("Positioning fault");
driverError = 1;
errlogSevPrintf(errlogMajor, "Run failure at %d", axisNo_);
updateMsgTxtFromDriver("Run failure");
comStatus = asynError;
errorReported = 1;
goto skip;
}
*moving = false;
setIntegerParam(pC_->motorStatusDone_, true);
//updateMsgTxtFromDriver("Believed to be on position");
} else {
if(oredMSR & 0x400){
setIntegerParam(pC_->motorStatusProblem_, true);
errlogSevPrintf(errlogMajor, "Positioning failure at %d", axisNo_);
updateMsgTxtFromDriver("Positioning failure");
comStatus = asynError;
errorReported = 1;
goto skip;
}
if(oredMSR & 0x200 || oredMSR & 0x80) {
errlogSevPrintf(errlogMinor, "Positioning fault at %d", axisNo_);
}
setIntegerParam(pC_->motorStatusProblem_, false);
} else {
*moving = true;
next_poll = -1;
setIntegerParam(pC_->motorStatusDone_, false);
//updateMsgTxtFromDriver("Creeping");
}
}
skip:
if(driverError == 0){
updateMsgTxtFromDriver(NULL);
}
setIntegerParam(pC_->motorStatusProblem_, comStatus ? 1:0);
callParamCallbacks();
return comStatus ? asynError : asynSuccess;
return comStatus;
}
/** Code for iocsh registration */

View File

@ -34,9 +34,7 @@ private:
double position;
int homing;
time_t next_poll;
int ErrTxtIdx;
void forwardErrorTxt(EL734Axis *axis);
int errorReported;
friend class EL734Controller;
};

View File

@ -0,0 +1,678 @@
/*
FILENAME... EuroMoveDriver.cpp
USAGE... Motor driver support for the EuroMove motor controller from LLB.
This is a driver for the EuroMove motor controller from LLB. This device talks to us either via
GPIB or USB. The controller is fairly simple. It can control quite a number of motors.
Reworked in 06/2021 to work with the Prologix GPIV-Ethernet converter
mark.koennecke@psi.ch
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <time.h>
#include <unistd.h>
#include <iocsh.h>
#include <epicsThread.h>
#include <errlog.h>
#include <asynOctetSyncIO.h>
#include "EuroMoveDriver.h"
#include <epicsExport.h>
#define IDLEPOLL 60
/** Creates a new EuroMoveController object.
* \param[in] portName The name of the asyn port that will be created for this driver
* \param[in] EuroMovePortName The name of the drvAsynSerialPort that was created previously to connect to the EuroMove controller
* \param[in] numAxes The number of axes that this controller supports
*/
EuroMoveController::EuroMoveController(const char *portName, const char *EuroMovePortName, int gpibAddr, int numAxis)
: SINQController(portName, EuroMovePortName,numAxis+1)
{
asynStatus status;
static const char *functionName = "EuroMoveController::EuroMoveController";
size_t out, in;
int reason;
char command[80], reply[80];
/* Connect to EuroMove controller */
status = pasynOctetSyncIO->connect(EuroMovePortName, 0, &pasynUserController_, NULL);
if (status) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s: cannot connect to EuroMove controller\n",
functionName);
}
const char *terminator = "\r\n";
pasynOctetSyncIO->setOutputEos(pasynUserController_,terminator,strlen(terminator));
const char *repTerminator = "\r";
pasynOctetSyncIO->setInputEos(pasynUserController_,repTerminator,strlen(repTerminator));
/* Save gpib address and prepare addr string */
this->gpibAddr = gpibAddr;
snprintf(this->addrCommand,sizeof(addrCommand), "++addr %d", gpibAddr);
/*
Configure the Prologix interface to our liking
The thing sends no response on configuration commands
*/
strcpy(command,"++mode 1"); /* Make it a controller */
status = pasynOctetSyncIO->write(pasynUserController_, command, strlen(command), 2, &out);
strcpy(command,"++auto 1"); /* ask for response automatically */
status = pasynOctetSyncIO->write(pasynUserController_, command, strlen(command), 2, &out);
strcpy(command,"++eos 1"); /* CR */
status = pasynOctetSyncIO->write(pasynUserController_, command, strlen(command), 2, &out);
for(int i = 0; i < numAxis; i++){
new EuroMoveAxis(this, i+1);
}
startPoller(1000./1000., IDLEPOLL, 2);
}
/** Creates a new EuroMoveController object.
* Configuration command, called directly or from iocsh
* \param[in] portName The name of the asyn port that will be created for this driver
* \param[in] EuroMovePortName The name of the drvAsynIPPPort that was created previously to connect to the EuroMove controller
* \param[in] gpibAddr The address of the controller on the GPIB bus
* \param[in] numAxes The number of axes that this controller supports
*/
extern "C" int EuroMoveCreateController(const char *portName, const char *EuroMovePortName, const int gpibAddr, const int numAxis)
{
new EuroMoveController(portName, EuroMovePortName, gpibAddr, numAxis);
return(asynSuccess);
}
/** Reports on status of the driver
* \param[in] fp The file pointer on which report information will be written
* \param[in] level The level of report detail desired
*
* If details > 0 then information is printed about each axis.
* After printing controller-specific information it calls asynMotorController::report()
*/
void EuroMoveController::report(FILE *fp, int level)
{
fprintf(fp, "EuroMove motor driver %s, numAxes=%d\n",
this->portName, numAxes_);
// Call the base class method
asynMotorController::report(fp, level);
}
/** Returns a pointer to an EuroMoveAxis object.
* Returns NULL if the axis number encoded in pasynUser is invalid.
* \param[in] pasynUser asynUser structure that encodes the axis index number. */
EuroMoveAxis* EuroMoveController::getAxis(asynUser *pasynUser)
{
return static_cast<EuroMoveAxis*>(asynMotorController::getAxis(pasynUser));
}
/** Returns a pointer to an EuroMoveAxis object.
* Returns NULL if the axis number encoded in pasynUser is invalid.
* \param[in] axisNo Axis index number. */
EuroMoveAxis* EuroMoveController::getAxis(int axisNo)
{
return static_cast<EuroMoveAxis*>(asynMotorController::getAxis(axisNo));
}
/**
* send a command to the EuroMove and read the reply. Do test for errors.
* \param[in] command The command to send
* \param[out] reply The controllers reply
*/
asynStatus EuroMoveController::transactController(int axisNo,char command[COMLEN], char reply[COMLEN])
{
asynStatus status;
SINQAxis *axis = getAxis(axisNo);
size_t out, in;
int reason, errCode, lCode;
char pDummy[50], lReply[50];
const char *lCommand = {"L"};
pasynOctetSyncIO->flush(pasynUserController_);
/* set address first */
status = pasynOctetSyncIO->write(pasynUserController_, this->addrCommand, strlen(this->addrCommand), 2, &out);
if(status != asynSuccess){
if(axis!= NULL){
axis->updateMsgTxtFromDriver("Lost connection to motor controller");
errlogPrintf("Lost connection to motor controller\n");
}
return status;
}
/* now the actual command */
status = pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
reply,sizeof(reply), 10.,&out,&in,&reason);
if(status != asynSuccess){
if(axis!= NULL){
axis->updateMsgTxtFromDriver("Lost connection to motor controller");
errlogPrintf("Lost connection to motor controller\n");
}
return status;
}
if(strstr(reply,"ERROR") != NULL){
sscanf((const char *)reply, "%s %d", pDummy, &errCode);
switch(errCode){
case 0:
axis->updateMsgTxtFromDriver("Syntax error or parameter out of range");
errlogPrintf("Syntax error or parameter out or range on %d\n", axisNo);
break;
case 1:
axis->updateMsgTxtFromDriver("Timeout communicating with daughter board");
errlogPrintf("Timeout communicating with daughter board on %d\n", axisNo);
break;
case 2:
axis->updateMsgTxtFromDriver("This is not a drivable motor");
errlogPrintf("This is not a drivable motor on %d\n", axisNo);
break;
case 3:
axis->updateMsgTxtFromDriver("Encoder anomaly");
errlogPrintf("Encoder anomaly on %d\n", axisNo);
break;
case 4:
axis->updateMsgTxtFromDriver("Attempt to move across limit switch");
errlogPrintf("Attempt to move across limit switch on %d\n", axisNo);
break;
case 5:
axis->updateMsgTxtFromDriver("Badly configured relay system");
errlogPrintf("Badly configured relay system on %d\n", axisNo);
break;
case 6:
axis->updateMsgTxtFromDriver("Non valid backup");
errlogPrintf("Invalid backup on %d\n", axisNo);
break;
case 7:
axis->updateMsgTxtFromDriver("Backup failed");
errlogPrintf("Backup failed on %d\n", axisNo);
break;
}
return asynError;
}
/*
Test system status
*/
status = pasynOctetSyncIO->writeRead(pasynUserController_, lCommand, strlen(lCommand),
lReply,sizeof(lReply), 1.,&out,&in,&reason);
if(status != asynSuccess){
if(axis!= NULL){
axis->updateMsgTxtFromDriver("Lost connection to motor controller");
errlogPrintf("Lost connection to motor controller\n");
}
return status;
}
sscanf(lReply, "%x", &lCode);
//errlogPrintf("System status returned %s, converted to %d\n", lReply, lCode);
if((lCode & 1) > 0){
axis->updateMsgTxtFromDriver("Syntax error or impossible to execute");
errlogPrintf("Syntax error or impossible to execute for %s on %d\n", command, axisNo);
return asynError;
}
return status;
}
asynStatus EuroMoveController::writeFloat64(asynUser *pasynUser, epicsFloat64 value)
{
int function = pasynUser->reason;
asynStatus status = asynError;
EuroMoveAxis *pAxis = NULL;
static const char *functionName = "EuroMoveController::writeFloat64";
pAxis = (EuroMoveAxis *)this->getAxis(pasynUser);
if (!pAxis) {
return asynError;
}
/* Set the parameter and readback in the parameter library. */
status = pAxis->setDoubleParam(function, value);
errlogPrintf("%s, reason %d, value %f\n", functionName, function, value);
// TODO: somethign is really shitty here: lowLimit and highLimit cannot be on the command
if (function == motorResolution_) {
if(value > .0){
pAxis->resolution = (int)(1./value);
} else {
pAxis->resolution = 1;
}
}
//Call base class method
//This will handle callCallbacks even if the function was handled here.
status = asynMotorController::writeFloat64(pasynUser, value);
return status;
}
// These are the EuroMoveAxis methods
/** Creates a new EuroMoveAxis object.
* \param[in] pC Pointer to the EuroMoveController to which this axis belongs.
* \param[in] axisNo Index number of this axis, range 0 to pC->numAxes_-1.
*
* Initializes register numbers, etc.
*/
EuroMoveAxis::EuroMoveAxis(EuroMoveController *pC, int axisNo)
: SINQAxis(pC, axisNo),
pC_(pC)
{
motNo = axisNo;
}
/** Reports on status of the axis
* \param[in] fp The file pointer on which report information will be written
* \param[in] level The level of report detail desired
*
* After printing device-specific information calls asynMotorAxis::report()
*/
void EuroMoveAxis::report(FILE *fp, int level)
{
if (level > 0) {
fprintf(fp, " axis %d\n",
axisNo_);
}
asynMotorAxis::report(fp, level);
}
asynStatus EuroMoveAxis::move(double position, int relative, double minVelocity, double maxVelocity, double acceleration)
{
asynStatus status;
//static const char *functionName = "EuroMoveAxis::move";
char command[COMLEN], reply[COMLEN];
updateMsgTxtFromDriver("");
if (relative) {
position += this->position;
}
homingStatus = HomeIdle;
sprintf(command,"G%d=%d", motNo, (int)position);
status = pC_->transactController(motNo,command,reply);
next_poll = -1;
targetPosition = (int)position;
return status;
}
asynStatus EuroMoveAxis::home(double minVelocity, double maxVelocity, double acceleration, int forwards)
{
asynStatus status = asynSuccess;
//static const char *functionName = "EuroMoveAxis::home";
char command[COMLEN], reply[COMLEN];
/*
The EuroMove controller does not have a built in homing mechanism. We simulate it by:
1). Running the motor in the appropriate end swicth at high speed
2) Stepping back three units
3) Run the motor again in the end switch at low speed for better accuracy
4) Set the limit position with the <I> command
The state of this operation is maintained in the homingStatus variable
*/
homingDirection = forwards;
homingStatus = HomeFastRun;
if(homingDirection){
sprintf(command,"H%d=03", motNo);
} else {
sprintf(command,"H%d=07", motNo);
}
status = pC_->transactController(motNo,command,reply);
next_poll = -1;
return status;
}
asynStatus EuroMoveAxis::moveVelocity(double minVelocity, double maxVelocity, double acceleration)
{
asynStatus status;
double target;
//static const char *functionName = "EuroMoveAxis::moveVelocity";
// asynPrint(pasynUser_, ASYN_TRACE_FLOW,
// "%s: minVelocity=%f, maxVelocity=%f, acceleration=%f\n",
// functionName, minVelocity, maxVelocity, acceleration);
updateMsgTxtFromDriver("");
if (maxVelocity > 0.) {
/* This is a positive move */
pC_->getDoubleParam(axisNo_,pC_->motorHighLimit_,&target);
} else {
/* This is a negative move */
pC_->getDoubleParam(axisNo_,pC_->motorLowLimit_,&target);
}
status = move(target,0,0,0,0);
next_poll = -1;
return status;
}
asynStatus EuroMoveAxis::sendStop()
{
asynStatus status;
char command[COMLEN], reply[COMLEN];
sprintf(command, "B%d", motNo);
status = pC_->transactController(axisNo_,command,reply);
return status;
}
asynStatus EuroMoveAxis::stop(double acceleration )
{
asynStatus status;
// static const char *functionName = "EuroMoveAxis::stop";
status = sendStop();
errlogPrintf("Sent STOP on Axis %d\n", axisNo_);
updateMsgTxtFromDriver("Axis interrupted");
return status;
}
asynStatus EuroMoveAxis::setPosition(double position)
{
asynStatus status;
//static const char *functionName = "EuroMoveAxis::setPosition";
char command[COMLEN], reply[COMLEN];
errlogPrintf("EuroMoveAxis::setPosition called with %lf\n", position);
sprintf(command, "I%d=%d",motNo,(int)position);
status = pC_->transactController(axisNo_,command,reply);
next_poll = -1;
return status;
}
asynStatus EuroMoveAxis::setClosedLoop(bool closedLoop)
{
//static const char *functionName = "EuroMoveAxis::setClosedLoop";
/*
This belongs into the Kingdom of Electronics.
We do not do this.
*/
return asynError;
}
/** Polls the axis.
* This function reads the motor position, the limit status, the home status, the moving status,
* and the drive power-on status.
* It calls setIntegerParam() and setDoubleParam() for each item that it polls,
* and then calls callParamCallbacks() at the end.
* \param[out] moving A flag that is set indicating that the axis is moving (true) or done (false). */
asynStatus EuroMoveAxis::poll(bool *moving)
{
asynStatus comStatus = asynSuccess;
char command[COMLEN], reply[COMLEN];
unsigned int axStatus;
double backStep, backPos, limit;
// protect against excessive polling
if(time(NULL) < next_poll){
*moving = false;
return asynSuccess;
}
setIntegerParam(pC_->motorStatusProblem_, false);
/*
read the current position
*/
sprintf(command,"A%d",motNo);
comStatus = pC_->transactController(axisNo_,command,reply);
if(comStatus == asynError) {
setIntegerParam(pC_->motorStatusProblem_, true);
goto skip;
}
sscanf(reply,"%d",&position);
setDoubleParam(pC_->motorPosition_,(double)position);
// Read the moving status of this motor
sprintf(command,"E%d",motNo);
comStatus = pC_->transactController(axisNo_,command,reply);
if(comStatus == asynError){
setIntegerParam(pC_->motorStatusProblem_, true);
goto skip;
}
/* errlogPrintf("Axis %d, status reply %s, position %d\n", axisNo_, reply, position); */
sscanf(reply, "%x", &axStatus);
if(homingStatus == HomeIdle){
if((axStatus & 128) > 0) { // test bit 8
*moving = true;
next_poll = -1;
setIntegerParam(pC_->motorStatusDone_, false);
} else {
*moving = false;
next_poll = time(NULL)+IDLEPOLL;
setIntegerParam(pC_->motorStatusDone_, true);
}
/* Testing limit switches */
if((axStatus & 2) > 0){ // bit 2
if(targetPosition > position) {
/*
Error codition only when we wish to move further
*/
setIntegerParam(pC_->motorStatusHighLimit_, true);
errlogPrintf("HighLimit detected on %d\n", motNo);
updateMsgTxtFromDriver("On high limit switch");
sendStop();
}
} else {
setIntegerParam(pC_->motorStatusHighLimit_, false);
}
if((axStatus & 1) > 0){ // bit 1
setIntegerParam(pC_->motorStatusLowLimit_, true);
if(targetPosition < position) {
/*
Error treatment onlly when we want to go below the limits
*/
errlogPrintf("LowLimit detected on %d\n", motNo);
sendStop();
updateMsgTxtFromDriver("On low limit switch");
}
} else {
setIntegerParam(pC_->motorStatusLowLimit_, false);
}
/* there could be errors reported in the motor status */
if((axStatus & 8) > 0){ // bit 4
setIntegerParam(pC_->motorStatusProblem_, true);
updateMsgTxtFromDriver("Internal timeout detected");
errlogPrintf("Internal timeout detected on %d\n", motNo);
comStatus = asynError;
}
if((axStatus & 4) > 0){ // bit 3
setIntegerParam(pC_->motorStatusProblem_, true);
updateMsgTxtFromDriver("Encoding anomaly");
errlogPrintf("Encoding anomaly on %d\n", motNo);
comStatus = asynError;
}
} else {
next_poll = -1;
setIntegerParam(pC_->motorStatusDone_, false);
*moving = true;
switch(homingStatus){
case HomeIdle:
// handled above: this is here to silence the compiler
break;
case HomeFastRun:
// errlogPrintf("HomeFastRun\n");
if(axStatus & 2 || axStatus & 1){
sendStop();
homingStatus = HomeFastStop;
}
break;
case HomeFastStop:
// errlogPrintf("HomeFastStop\n");
if((axStatus & 128) == 0){
backStep = 3 * resolution;
if(homingDirection){
backPos = position - backStep;
} else {
backPos = position + backStep;
}
sprintf(command,"G%d=%d", motNo, (int)backPos);
comStatus = pC_->transactController(motNo,command,reply);
if(comStatus == asynError){
setIntegerParam(pC_->motorStatusProblem_, true);
updateMsgTxtFromDriver("Homing problem when back stepping");
errlogPrintf("Homing problem when back stepping: %s not accepted\n", command);
*moving = false;
setIntegerParam(pC_->motorStatusAtHome_, true);
homingStatus = HomeIdle;
setIntegerParam(pC_->motorStatusDone_, true);
goto skip;
}
usleep(200);
homingStatus = HomeBackStep;
}
break;
case HomeBackStep:
//errlogPrintf("HomeBackStep\n");
if((axStatus & 128) == 0){
homingStatus = HomeSlowRun;
if(homingDirection){
sprintf(command,"H%d=01", motNo);
} else {
sprintf(command,"H%d=05", motNo);
}
comStatus = pC_->transactController(motNo,command,reply);
if(comStatus == asynError){
setIntegerParam(pC_->motorStatusProblem_, true);
updateMsgTxtFromDriver("Homing problem slow running");
errlogPrintf("Homing problem when slow running: %s not accepted\n", command);
*moving = false;
setIntegerParam(pC_->motorStatusAtHome_, true);
homingStatus = HomeIdle;
setIntegerParam(pC_->motorStatusDone_, true);
goto skip;
}
}
break;
case HomeSlowRun:
//errlogPrintf("HomeSlowRun\n");
if(axStatus & 2 || axStatus & 1){
sendStop();
homingStatus = HomeSlowStop;
}
break;
case HomeSlowStop:
//errlogPrintf("HomeSlowStop\n");
if((axStatus & 128) == 0) {
if(homingDirection) {
pC_->getDoubleParam(axisNo_,pC_->motorHighLimit_,&limit);
} else {
pC_->getDoubleParam(axisNo_,pC_->motorLowLimit_,&limit);
}
setPosition(limit);
*moving = false;
setIntegerParam(pC_->motorStatusDone_, true);
next_poll = time(NULL)+IDLEPOLL;
setIntegerParam(pC_->motorStatusDone_, true);
setIntegerParam(pC_->motorStatusAtHome_, true);
homingStatus = HomeIdle;
setDoubleParam(pC_->motorPosition_,(double)limit);
}
}
}
skip:
callParamCallbacks();
return comStatus;
}
/** Code for configuring the motNo on axis **/
extern "C" {
asynStatus EuroMoveConfigureAxis(const char *euroMoveName, unsigned int axisNo, unsigned int motNo)
{
EuroMoveController *pC = NULL;
EuroMoveAxis *pAxis = NULL;
pC = (EuroMoveController *)findAsynPortDriver(euroMoveName);
if(!pC){
errlogPrintf("%s driver not found", euroMoveName);
return asynError;
}
pC->unlock();
pAxis = (EuroMoveAxis *)pC->getAxis(axisNo);
if(!pAxis){
errlogPrintf("axis %d driver not found on %s", axisNo, euroMoveName);
return asynError;
}
pAxis->setMotNo(motNo);
return asynSuccess;
}
}
static const iocshArg EuroMoveConfigArg0 = {"Port name", iocshArgString};
static const iocshArg EuroMoveConfigArg1 = {"Axis Index", iocshArgInt};
static const iocshArg EuroMoveConfigArg2 = {"Motor number in EuroMove", iocshArgInt};
static const iocshArg * const EuroMoveConfigArgs[] = {&EuroMoveConfigArg0,
&EuroMoveConfigArg1,
&EuroMoveConfigArg2
};
static const iocshFuncDef EuroMoveConfigDef = {"EuroMoveConfigureAxis", 3, EuroMoveConfigArgs};
static void EuroMoveConfigCallFunc(const iocshArgBuf *args)
{
EuroMoveConfigureAxis(args[0].sval, (unsigned int)args[1].ival, (unsigned int)args[2].ival);
}
/** Code for iocsh registration */
static const iocshArg EuroMoveCreateControllerArg0 = {"Port name", iocshArgString};
static const iocshArg EuroMoveCreateControllerArg1 = {"EuroMove port name", iocshArgString};
static const iocshArg EuroMoveCreateControllerArg2 = {"GPIB Address", iocshArgInt};
static const iocshArg EuroMoveCreateControllerArg3 = {"Number of axis", iocshArgInt};
static const iocshArg * const EuroMoveCreateControllerArgs[] = {&EuroMoveCreateControllerArg0,
&EuroMoveCreateControllerArg1,
&EuroMoveCreateControllerArg2,
&EuroMoveCreateControllerArg3
};
static const iocshFuncDef EuroMoveCreateControllerDef = {"EuroMoveCreateController", 4, EuroMoveCreateControllerArgs};
static void EuroMoveCreateControllerCallFunc(const iocshArgBuf *args)
{
EuroMoveCreateController(args[0].sval, args[1].sval, args[2].ival, args[3].ival);
}
static void EuroMoveRegister(void)
{
iocshRegister(&EuroMoveCreateControllerDef, EuroMoveCreateControllerCallFunc);
iocshRegister(&EuroMoveConfigDef, EuroMoveConfigCallFunc);
}
extern "C" {
epicsExportRegistrar(EuroMoveRegister);
}

View File

@ -0,0 +1,72 @@
/*
FILENAME... EuroMoveDriver.h
USAGE... Motor driver support for the LLB EuroMove controller
Mark Koennecke
January 2020
*/
#include "SINQController.h"
#include "SINQAxis.h"
#define COMLEN 80
typedef enum {
HomeIdle,
HomeFastRun,
HomeFastStop,
HomeBackStep,
HomeSlowRun,
HomeSlowStop,
} HomingStatus;
class EuroMoveAxis : public SINQAxis
{
public:
/* These are the methods we override from the base class */
EuroMoveAxis(class EuroMoveController *pC, int axis);
void report(FILE *fp, int level);
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration);
asynStatus moveVelocity(double min_velocity, double max_velocity, double acceleration);
asynStatus home(double min_velocity, double max_velocity, double acceleration, int forwards);
asynStatus stop(double acceleration);
asynStatus poll(bool *moving);
asynStatus setPosition(double position);
asynStatus setClosedLoop(bool closedLoop);
void setMotNo(unsigned int no){
motNo = no;
}
private:
EuroMoveController *pC_; /**< Pointer to the asynMotorController to which this axis belongs.
* Abbreviated because it is used very frequently */
time_t next_poll;
int motNo; // motor number in the EuroMove controller
int position;
HomingStatus homingStatus;
int homingDirection; // 1 = forward, 0 backwards
asynStatus sendStop();
int resolution;
friend class EuroMoveController;
int targetPosition;
};
class EuroMoveController : public SINQController {
public:
EuroMoveController(const char *portName, const char *EuroMovePortName, const int gpibAddr, const int nAxis);
void report(FILE *fp, int level);
EuroMoveAxis* getAxis(asynUser *pasynUser);
EuroMoveAxis* getAxis(int axisNo);
asynStatus writeFloat64(asynUser *pasynUser, epicsFloat64 value);
friend class EuroMoveAxis;
private:
asynUser *pasynUserController_;
unsigned int gpibAddr;
char addrCommand[50];
asynStatus transactController(int axisNo, char command[COMLEN], char reply[COMLEN]);
};

View File

@ -28,6 +28,7 @@ sinqEPICS_SRCS += EL734Driver.cpp devScalerEL737.c pmacAsynIPPort.c SINQAxis.cpp
sinqEPICS_SRCS += pmacController.cpp pmacAxis.cpp
sinqEPICS_SRCS += NanotecDriver.cpp stptok.cpp
sinqEPICS_SRCS += PhytronDriver.cpp
sinqEPICS_SRCS += EuroMoveDriver.cpp
# Build the main IOC entry point on workstation OSs.

View File

@ -14,6 +14,9 @@ list to the motor controller constructor.
Mark Koennecke
July 2015
Modified to use the MsgTxt field for SINQ
Mark Koennecke, January 2019
*/
@ -43,12 +46,7 @@ July 2015
* \param[in] NanotecPortName The name of the drvAsynSerialPort that was created previously to connect to the Nanotec controller
*/
NanotecController::NanotecController(const char *portName, const char *NanotecPortName, int motCount, const char *bus)
: asynMotorController(portName, motCount+1, 0,
0, // No additional interfaces beyond those in base class
0, // No additional callback interfaces beyond those in base class
ASYN_CANBLOCK | ASYN_MULTIDEVICE,
1, // autoconnect
0, 0) // Default priority and stack size
: SINQController(portName, NanotecPortName, motCount+1)
{
int axis, busAddress;
asynStatus status;
@ -136,7 +134,7 @@ NanotecAxis* NanotecController::getAxis(int axisNo)
* Initializes register numbers, etc.
*/
NanotecAxis::NanotecAxis(NanotecController *pC, int axisNo, int busAddress)
: asynMotorAxis(pC, axisNo),
: SINQAxis(pC, axisNo),
pC_(pC)
{
this->busAddress = busAddress;
@ -162,17 +160,21 @@ void NanotecAxis::report(FILE *fp, int level)
//asynMotorAxis::report(fp, level);
}
asynStatus NanotecController::transactController(char command[COMLEN], char reply[COMLEN])
asynStatus NanotecController::transactController(int axisNo, char command[COMLEN], char reply[COMLEN])
{
asynStatus status;
size_t in, out;
int reason;
SINQAxis *axis = getAxis(axisNo);
pasynOctetSyncIO->flush(pasynUserController_);
status = pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
reply,COMLEN, 1.,&out,&in,&reason);
if(status != asynSuccess){
if(axis != NULL){
axis->updateMsgTxtFromDriver("Lost connection to motor controller");
}
return status;
}
@ -200,6 +202,8 @@ asynStatus NanotecAxis::move(double position, int relative, double minVelocity,
size_t in, out;
int reason;
updateMsgTxtFromDriver("");
// status = sendAccelAndVelocity(acceleration, maxVelocity);
if (relative) {
@ -215,7 +219,7 @@ asynStatus NanotecAxis::move(double position, int relative, double minVelocity,
set mode
*/
snprintf(command,sizeof(command),"#%dp2",busAddress);
status = pC_->transactController(command,reply);
status = pC_->transactController(axisNo_,command,reply);
if(status != asynSuccess){
return status;
}
@ -224,7 +228,7 @@ asynStatus NanotecAxis::move(double position, int relative, double minVelocity,
set target
*/
snprintf(command,sizeof(command),"#%ds%d",busAddress, (int)position);
status = pC_->transactController(command,reply);
status = pC_->transactController(axisNo_,command,reply);
if(status != asynSuccess){
return status;
}
@ -233,7 +237,7 @@ asynStatus NanotecAxis::move(double position, int relative, double minVelocity,
and start..
*/
snprintf(command,sizeof(command),"#%dA",busAddress);
status = pC_->transactController(command,reply);
status = pC_->transactController(axisNo_,command,reply);
if(status != asynSuccess){
return status;
}
@ -250,11 +254,13 @@ asynStatus NanotecAxis::home(double minVelocity, double maxVelocity, double acce
setIntegerParam(pC_->motorStatusAtHome_, false);
updateMsgTxtFromDriver("");
/*
reset positioning errors
*/
snprintf(command,sizeof(command),"#%dD",busAddress);
status = pC_->transactController(command,reply);
status = pC_->transactController(axisNo_,command,reply);
if(status != asynSuccess){
return status;
}
@ -263,7 +269,7 @@ asynStatus NanotecAxis::home(double minVelocity, double maxVelocity, double acce
set mode
*/
snprintf(command,sizeof(command),"#%dp4",busAddress);
status = pC_->transactController(command,reply);
status = pC_->transactController(axisNo_,command,reply);
if(status != asynSuccess){
return status;
}
@ -273,7 +279,7 @@ asynStatus NanotecAxis::home(double minVelocity, double maxVelocity, double acce
set direction
*/
snprintf(command,sizeof(command),"#%dd0",busAddress);
status = pC_->transactController(command,reply);
status = pC_->transactController(axisNo_,command,reply);
if(status != asynSuccess){
return status;
}
@ -283,7 +289,7 @@ asynStatus NanotecAxis::home(double minVelocity, double maxVelocity, double acce
and start..
*/
snprintf(command,sizeof(command),"#%dA",busAddress);
status = pC_->transactController(command,reply);
status = pC_->transactController(axisNo_,command,reply);
if(status != asynSuccess){
return status;
}
@ -304,6 +310,7 @@ asynStatus NanotecAxis::moveVelocity(double minVelocity, double maxVelocity, dou
// "%s: minVelocity=%f, maxVelocity=%f, acceleration=%f\n",
// functionName, minVelocity, maxVelocity, acceleration);
updateMsgTxtFromDriver("");
if (maxVelocity > 0.) {
@ -326,7 +333,7 @@ asynStatus NanotecAxis::stop(double acceleration )
char command[COMLEN], reply[COMLEN];
sprintf(command, "#%dS1", busAddress);
status = pC_->transactController(command,reply);
status = pC_->transactController(axisNo_,command,reply);
errlogPrintf("Sent STOP on Axis %d\n", axisNo_);
return status;
@ -338,8 +345,10 @@ asynStatus NanotecAxis::setPosition(double position)
//static const char *functionName = "NanotecAxis::setPosition";
char command[COMLEN], reply[COMLEN];
updateMsgTxtFromDriver("");
sprintf(command, "#%dD%d", busAddress, (int)position);
status = pC_->transactController(command,reply);
status = pC_->transactController(axisNo_,command,reply);
next_poll = -1;
return status;
@ -380,28 +389,28 @@ asynStatus NanotecAxis::poll(bool *moving)
// Read the current motor position
sprintf(command,"#%dC", busAddress);
comStatus = pC_->transactController(command,reply);
comStatus = pC_->transactController(axisNo_,command,reply);
if(comStatus) goto skip;
pPtr = strchr(reply,'C');
pPtr++;
posVal = atoi(pPtr);
errlogPrintf("Axis %d, reply %s, position %d\n", axisNo_, reply, posVal);
//errlogPrintf("Axis %d, reply %s, position %d\n", axisNo_, reply, posVal);
setDoubleParam(pC_->motorPosition_, (double)posVal);
//setDoubleParam(pC_->motorEncoderPosition_, position);
// Read the moving status of this motor
sprintf(command,"#%d$",busAddress);
comStatus = pC_->transactController(command,reply);
comStatus = pC_->transactController(axisNo_,command,reply);
if(comStatus) goto skip;
pPtr = strchr(reply,'$');
pPtr++;
statVal = atoi(pPtr);
errlogPrintf("Axis %d, reply %s, statVal = %d\n",
axisNo_, reply, statVal);
//errlogPrintf("Axis %d, reply %s, statVal = %d\n",
// axisNo_, reply, statVal);
setIntegerParam(pC_->motorStatusDone_, false);
*moving = true;
@ -438,10 +447,13 @@ asynStatus NanotecAxis::poll(bool *moving)
setIntegerParam(pC_->motorStatusDone_, true);
setIntegerParam(pC_->motorStatusProblem_, true);
errlogSevPrintf(errlogMajor, "Limit or other positioning problem at %d", axisNo_);
updateMsgTxtFromDriver("Positioning problem");
if(ABS(posVal - lowLim) < ABS(posVal - highLim)){
setIntegerParam(pC_->motorStatusLowLimit_, true);
updateMsgTxtFromDriver("Low Limit Hit");
} else {
setIntegerParam(pC_->motorStatusHighLimit_, true);
updateMsgTxtFromDriver("High Limit Hit");
}
*moving = false;
}

View File

@ -5,15 +5,18 @@ USAGE... Motor driver support for the Nanotec SMCI controller.
Mark Koennecke
July 2015
Modified to use the MsgTxt field for SINQ
Mark Koennecke, January 2019
*/
#include "asynMotorController.h"
#include "asynMotorAxis.h"
#include "SINQController.h"
#include "SINQAxis.h"
#define COMLEN 80
#define MAXMOT 99
class NanotecAxis : public asynMotorAxis
class NanotecAxis : public SINQAxis
{
public:
/* These are the methods we override from the base class */
@ -38,7 +41,7 @@ private:
friend class NanotecController;
};
class NanotecController : public asynMotorController {
class NanotecController : public SINQController {
public:
NanotecController(const char *portName, const char *NanotecPortName, int numMot, const char *busAddresses);
@ -50,7 +53,7 @@ friend class NanotecAxis;
private:
asynUser *pasynUserController_;
asynStatus transactController(char command[COMLEN], char reply[COMLEN]);
asynStatus transactController(int axisNo, char command[COMLEN], char reply[COMLEN]);

View File

@ -23,6 +23,12 @@ Though this driver has been written in 2016, the MCC-2 version used is probably
Mark Koennecke
September 2016
Updated to use the new MsgTxt field through SINQAxis
Added a selector to support multiple phytrons on a connection
Mark Koennecke
January 2019
*/
@ -49,19 +55,16 @@ September 2016
* \param[in] PhytronPortName The name of the drvAsynSerialPort that was created previously to connect to the Phytron controller
* \param[in] numAxes The number of axes that this controller supports
*/
PhytronController::PhytronController(const char *portName, const char *PhytronPortName, int encX, int encY)
: asynMotorController(portName, 3, 0,
0, // No additional interfaces beyond those in base class
0, // No additional callback interfaces beyond those in base class
ASYN_CANBLOCK | ASYN_MULTIDEVICE,
1, // autoconnect
0, 0) // Default priority and stack size
PhytronController::PhytronController(const char *portName, const char *PhytronPortName, const char *sel ,
int encX, int encY)
: SINQController(portName, PhytronPortName,2)
{
asynStatus status;
PhytronAxis *pAxis;
static const char *functionName = "PhytronController::PhytronController";
char etx[2];
selector = strdup(sel);
/* Connect to Phytron controller */
status = pasynOctetSyncIO->connect(PhytronPortName, 0, &pasynUserController_, NULL);
if (status) {
@ -74,8 +77,8 @@ PhytronController::PhytronController(const char *portName, const char *PhytronPo
pasynOctetSyncIO->setOutputEos(pasynUserController_,etx,strlen(etx));
pasynOctetSyncIO->setInputEos(pasynUserController_,etx,strlen(etx));
pAxis = new PhytronAxis(this, 1, encX);
pAxis = new PhytronAxis(this, 2, encY);
new PhytronAxis(this, 1, encX);
new PhytronAxis(this, 2, encY);
startPoller(1000./1000., IDLEPOLL, 2);
}
@ -87,11 +90,10 @@ PhytronController::PhytronController(const char *portName, const char *PhytronPo
* \param[in] PhytronPortName The name of the drvAsynIPPPort that was created previously to connect to the Phytron controller
* \param[in] numAxes The number of axes that this controller supports
*/
extern "C" int PhytronCreateController(const char *portName, const char *PhytronPortName, int encX, int encY)
extern "C" int PhytronCreateController(const char *portName, const char *PhytronPortName, const char *selector,
int encX, int encY)
{
PhytronController *pPhytronController
= new PhytronController(portName, PhytronPortName, encX, encY);
pPhytronController = NULL;
new PhytronController(portName, PhytronPortName,selector, encX, encY);
return(asynSuccess);
}
@ -134,12 +136,14 @@ PhytronAxis* PhytronController::getAxis(int axisNo)
* \param[out] reply The controllers reply
*/
asynStatus PhytronController::transactController(char command[COMLEN], char reply[COMLEN])
asynStatus PhytronController::transactController(int axisNo,char command[COMLEN], char reply[COMLEN])
{
asynStatus status;
size_t in, out;
int reason;
char myReply[COMLEN+10], myCommand[COMLEN+10], *pPtr;
SINQAxis *axis = getAxis(axisNo);
pasynOctetSyncIO->flush(pasynUserController_);
@ -155,6 +159,9 @@ asynStatus PhytronController::transactController(char command[COMLEN], char repl
status = pasynOctetSyncIO->writeRead(pasynUserController_, myCommand, strlen(myCommand),
myReply,sizeof(myReply), 1.,&out,&in,&reason);
if(status != asynSuccess){
if(axis!= NULL){
axis->updateMsgTxtFromDriver("Lost connection to motor controller");
}
return status;
}
@ -176,7 +183,7 @@ asynStatus PhytronController::transactController(char command[COMLEN], char repl
I may need to replace the ETX. But I am not sure if asyn did
not remove it for me.
*/
strncat(reply,pPtr,sizeof(reply));
strncat(reply,pPtr,COMLEN-1);
return status;
@ -191,7 +198,7 @@ asynStatus PhytronController::transactController(char command[COMLEN], char repl
* Initializes register numbers, etc.
*/
PhytronAxis::PhytronAxis(PhytronController *pC, int axisNo, int enc)
: asynMotorAxis(pC, axisNo),
: SINQAxis(pC, axisNo),
pC_(pC)
{
encoder = enc;
@ -200,10 +207,20 @@ PhytronAxis::PhytronAxis(PhytronController *pC, int axisNo, int enc)
} else {
phytronChar = 'Y';
}
haveBrake = 0;
brakeIO = -1;
}
int PhytronAxis::setBrake(int brakeNO)
{
if(brakeNO < 1 || brakeNO > 8) {
return 0;
}
haveBrake = 1;
brakeIO = brakeNO;
return 1;
}
/** Reports on status of the axis
* \param[in] fp The file pointer on which report information will be written
@ -229,15 +246,46 @@ asynStatus PhytronAxis::move(double position, int relative, double minVelocity,
//static const char *functionName = "PhytronAxis::move";
char command[COMLEN], reply[COMLEN];
updateMsgTxtFromDriver("");
/*
deal with brake
*/
if(haveBrake) {
sprintf(command,"%sA%dS", pC_->selector, brakeIO);
status = pC_->transactController(axisNo_,command,reply);
if(strstr(reply,"NACK") != NULL){
errlogSevPrintf(errlogMajor, "Failed to release brake on %d", axisNo_);
updateMsgTxtFromDriver("Failed to release brake");
setIntegerParam(pC_->motorStatusProblem_, true);
return asynError;
}
}
/*
set speed
*/
sprintf(command, "%s%cP14S%f", pC_->selector, phytronChar, maxVelocity);
status = pC_->transactController(axisNo_,command,reply);
if(strstr(reply,"NACK") != NULL){
errlogSevPrintf(errlogMajor, "Speed not accepted on %d", axisNo_);
updateMsgTxtFromDriver("Invalid speed");
setIntegerParam(pC_->motorStatusProblem_, true);
return asynError;
}
/*
actually send a move command
*/
if (relative) {
position += this->position;
}
homing = 0;
sprintf(command, "0%cA%f", phytronChar,position/1000.);
status = pC_->transactController(command,reply);
sprintf(command, "%s%cA%f", pC_->selector,phytronChar,position/1000.);
status = pC_->transactController(axisNo_,command,reply);
if(strstr(reply,"NACK") != NULL){
errlogSevPrintf(errlogMajor, "Drive command not acknowledged on %d", axisNo_);
updateMsgTxtFromDriver("Drive command not acknowledged");
setIntegerParam(pC_->motorStatusProblem_, true);
return asynError;
}
@ -251,13 +299,47 @@ asynStatus PhytronAxis::home(double minVelocity, double maxVelocity, double acce
//static const char *functionName = "PhytronAxis::home";
char command[COMLEN], reply[COMLEN];
sprintf(command, "0%cO-",phytronChar);
updateMsgTxtFromDriver("");
/*
handle the fucking brake
*/
if(haveBrake) {
sprintf(command,"%sA%dS", pC_->selector, brakeIO);
status = pC_->transactController(axisNo_,command,reply);
if(strstr(reply,"NACK") != NULL){
errlogSevPrintf(errlogMajor, "Failed to release brake on %d", axisNo_);
updateMsgTxtFromDriver("Failed to release brake");
setIntegerParam(pC_->motorStatusProblem_, true);
return asynError;
}
}
/*
set speed
*/
sprintf(command, "%s%cP14S%f", pC_->selector, phytronChar, maxVelocity);
status = pC_->transactController(axisNo_,command,reply);
if(strstr(reply,"NACK") != NULL){
errlogSevPrintf(errlogMajor, "Speed not accepted on %d", axisNo_);
updateMsgTxtFromDriver("Invalid speed");
setIntegerParam(pC_->motorStatusProblem_, true);
return asynError;
}
homing_direction = forwards;
if(forwards){
sprintf(command, "%s%c0+",pC_->selector,phytronChar);
} else {
sprintf(command, "%s%c0-",pC_->selector,phytronChar);
}
homing = 1;
next_poll= -1;
status = pC_->transactController(command,reply);
status = pC_->transactController(axisNo_,command,reply);
if(strstr(reply,"NACK") != NULL){
errlogSevPrintf(errlogMajor, "Home command not acknowledged on %d", axisNo_);
setIntegerParam(pC_->motorStatusProblem_, true);
updateMsgTxtFromDriver("Home command not acknowledged");
return asynError;
}
return status;
@ -274,6 +356,7 @@ asynStatus PhytronAxis::moveVelocity(double minVelocity, double maxVelocity, dou
// "%s: minVelocity=%f, maxVelocity=%f, acceleration=%f\n",
// functionName, minVelocity, maxVelocity, acceleration);
updateMsgTxtFromDriver("");
if (maxVelocity > 0.) {
@ -294,9 +377,10 @@ asynStatus PhytronAxis::stop(double acceleration )
//static const char *functionName = "PhytronAxis::stop";
char command[COMLEN], reply[COMLEN];
sprintf(command, "0%cSN", phytronChar);
status = pC_->transactController(command,reply);
sprintf(command, "%s%cSN", pC_->selector,phytronChar);
status = pC_->transactController(axisNo_,command,reply);
errlogPrintf("Sent STOP on Axis %d\n", axisNo_);
updateMsgTxtFromDriver("Axis interrupted");
return status;
}
@ -307,10 +391,12 @@ asynStatus PhytronAxis::setPosition(double position)
//static const char *functionName = "PhytronAxis::setPosition";
char command[COMLEN], reply[COMLEN];
sprintf(command, "0%cP22S%f", phytronChar, position/1000.);
status = pC_->transactController(command,reply);
sprintf(command, "0%cP20S%f", phytronChar, position/1000.);
status = pC_->transactController(command,reply);
errlogPrintf("PhytronAxis::setPosition called with %lf\n", position);
sprintf(command, "%s%cP22S%f", pC_->selector,phytronChar, position/1000.);
status = pC_->transactController(axisNo_,command,reply);
sprintf(command, "%s%cP20S%f", pC_->selector,phytronChar, position/1000.);
status = pC_->transactController(axisNo_,command,reply);
next_poll = -1;
return status;
@ -336,7 +422,7 @@ asynStatus PhytronAxis::setClosedLoop(bool closedLoop)
* \param[out] moving A flag that is set indicating that the axis is moving (true) or done (false). */
asynStatus PhytronAxis::poll(bool *moving)
{
asynStatus comStatus;
asynStatus comStatus = asynSuccess;
char command[COMLEN], reply[COMLEN];
double lowlim;
@ -347,19 +433,24 @@ asynStatus PhytronAxis::poll(bool *moving)
return asynSuccess;
}
setIntegerParam(pC_->motorStatusProblem_, false);
// Read the current motor position
if(encoder) {
sprintf(command,"0%cP22R",phytronChar);
sprintf(command,"%s%cP22R",pC_->selector,phytronChar);
} else {
sprintf(command,"0%cP20R",phytronChar);
sprintf(command,"%s%cP20R",pC_->selector,phytronChar);
}
comStatus = pC_->transactController(command,reply);
if(comStatus) goto skip;
comStatus = pC_->transactController(axisNo_,command,reply);
if(comStatus == asynError) {
setIntegerParam(pC_->motorStatusProblem_, true);
updateMsgTxtFromDriver("No connection to phytron controller");
goto skip;
}
if(strstr(reply,"NACK") != NULL){
setIntegerParam(pC_->motorStatusProblem_, true);
errlogSevPrintf(errlogMajor, "Bad reply for position on %d", axisNo_);
updateMsgTxtFromDriver("Bad reply reading position");
goto skip;
}
/*
@ -372,9 +463,13 @@ asynStatus PhytronAxis::poll(bool *moving)
// Read the moving status of this motor
sprintf(command,"0%c=H",phytronChar);
comStatus = pC_->transactController(command,reply);
if(comStatus) goto skip;
sprintf(command,"%s%c=H",pC_->selector,phytronChar);
comStatus = pC_->transactController(axisNo_,command,reply);
if(comStatus == asynError){
setIntegerParam(pC_->motorStatusProblem_, true);
updateMsgTxtFromDriver("No connection to phytron controller");
goto skip;
}
/* errlogPrintf("Axis %d, status reply %s, position %lf\n", axisNo_, reply, position); */
if(strstr(reply,"ACKN") != NULL){
*moving = true;
@ -384,23 +479,45 @@ asynStatus PhytronAxis::poll(bool *moving)
*moving = false;
next_poll = time(NULL)+IDLEPOLL;
setIntegerParam(pC_->motorStatusDone_, true);
if(haveBrake) {
sprintf(command,"%sA%dR", pC_->selector, brakeIO);
comStatus = pC_->transactController(axisNo_,command,reply);
if(strstr(reply,"NACK") != NULL){
errlogSevPrintf(errlogMajor, "Failed to set brake on %d", axisNo_);
updateMsgTxtFromDriver("Failed to set brake");
setIntegerParam(pC_->motorStatusProblem_, true);
return asynError;
}
}
}
if(!*moving) {
if(homing){
pC_->getDoubleParam(axisNo_,pC_->motorLowLimit_,&lowlim);
if(homing_direction) {
pC_->getDoubleParam(axisNo_,pC_->motorHighLimit_,&lowlim);
} else {
pC_->getDoubleParam(axisNo_,pC_->motorLowLimit_,&lowlim);
}
setPosition(lowlim);
setIntegerParam(pC_->motorStatusAtHome_, true);
} else {
/*
check limits and errors, upper
*/
sprintf(command,"0%c=I+",phytronChar);
comStatus = pC_->transactController(command,reply);
if(comStatus) goto skip;
sprintf(command,"%s%c=I+",pC_->selector,phytronChar);
comStatus = pC_->transactController(axisNo_,command,reply);
if(comStatus == asynError) {
setIntegerParam(pC_->motorStatusProblem_, true);
updateMsgTxtFromDriver("No connection to phytron controller");
goto skip;
}
if(strstr(reply,"ACKE") != NULL){
setIntegerParam(pC_->motorStatusHighLimit_, true);
updateMsgTxtFromDriver("Hit High Limit");
comStatus = asynError;
goto skip;
} else {
setIntegerParam(pC_->motorStatusHighLimit_, false);
}
@ -408,11 +525,18 @@ asynStatus PhytronAxis::poll(bool *moving)
/*
lower limit
*/
sprintf(command,"0%c=I-",phytronChar);
comStatus = pC_->transactController(command,reply);
if(comStatus) goto skip;
sprintf(command,"%s%c=I-",pC_->selector,phytronChar);
comStatus = pC_->transactController(axisNo_,command,reply);
if(comStatus == asynError){
setIntegerParam(pC_->motorStatusProblem_, true);
updateMsgTxtFromDriver("No connection to phytron controller");
goto skip;
}
if(strstr(reply,"ACKE") != NULL){
setIntegerParam(pC_->motorStatusLowLimit_, true);
updateMsgTxtFromDriver("Low Limit Hit");
comStatus = asynError;
goto skip;
} else {
setIntegerParam(pC_->motorStatusLowLimit_, false);
}
@ -420,12 +544,19 @@ asynStatus PhytronAxis::poll(bool *moving)
/*
error
*/
sprintf(command,"0%c=E",phytronChar);
comStatus = pC_->transactController(command,reply);
if(comStatus) goto skip;
sprintf(command,"%s%c=E",pC_->selector,phytronChar);
comStatus = pC_->transactController(axisNo_,command,reply);
if(comStatus == asynError) {
setIntegerParam(pC_->motorStatusProblem_, true);
updateMsgTxtFromDriver("No connection to phytron controller");
goto skip;
}
if(strstr(reply,"ACKE") != NULL){
setIntegerParam(pC_->motorStatusProblem_, true);
errlogSevPrintf(errlogMajor, "Electronics on %d", axisNo_);
updateMsgTxtFromDriver("Electronics error");
comStatus = asynError;
goto skip;
} else {
setIntegerParam(pC_->motorStatusProblem_, false);
}
@ -434,29 +565,76 @@ asynStatus PhytronAxis::poll(bool *moving)
skip:
setIntegerParam(pC_->motorStatusProblem_, comStatus ? 1:0);
callParamCallbacks();
return comStatus ? asynError : asynSuccess;
return comStatus;
}
extern "C" asynStatus PhytronConfBrake(const char *port, /* specify which controller by port name */
int axis, /* axis: 0, 1 */
int brakeNO) /* brakeIO No, 1-8 */
{
PhytronController *pC;
PhytronAxis *pAxis;
int status;
static const char *functionName = "PhytronConfBrake";
pC = (PhytronController*) findAsynPortDriver(port);
if (!pC) {
printf("%s:%s: Error port %s not found\n",
"PhytronDriver", functionName, port);
return asynError;
}
pC->lock();
pAxis = pC->getAxis(axis);
status = pAxis->setBrake(brakeNO);
pC->unlock();
if(!status) {
printf("%s:%s:%s requested brake IO out of range\n",
"PhytronDriver", functionName, port);
return asynError;
}
return asynSuccess;
}
/** Code for iocsh registration */
static const iocshArg PhytronCreateControllerArg0 = {"Port name", iocshArgString};
static const iocshArg PhytronCreateControllerArg1 = {"Phytron port name", iocshArgString};
static const iocshArg PhytronCreateControllerArg2 = {"EnoderX", iocshArgInt};
static const iocshArg PhytronCreateControllerArg3 = {"EnoderY", iocshArgInt};
static const iocshArg PhytronCreateControllerArg2 = {"Phytron Selector", iocshArgString};
static const iocshArg PhytronCreateControllerArg3 = {"EncoderX", iocshArgInt};
static const iocshArg PhytronCreateControllerArg4 = {"EncoderY", iocshArgInt};
static const iocshArg * const PhytronCreateControllerArgs[] = {&PhytronCreateControllerArg0,
&PhytronCreateControllerArg1,
&PhytronCreateControllerArg2,
&PhytronCreateControllerArg3};
static const iocshFuncDef PhytronCreateControllerDef = {"PhytronCreateController", 4, PhytronCreateControllerArgs};
&PhytronCreateControllerArg3,
&PhytronCreateControllerArg4};
static const iocshFuncDef PhytronCreateControllerDef = {"PhytronCreateController", 5, PhytronCreateControllerArgs};
static void PhytronCreateContollerCallFunc(const iocshArgBuf *args)
{
PhytronCreateController(args[0].sval, args[1].sval, args[2].ival,args[3].ival);
PhytronCreateController(args[0].sval, args[1].sval, args[2].sval, args[3].ival,args[4].ival);
}
/* PhytronConfigureBrake */
static const iocshArg phytronBrakeArg0 = {"Controller port name", iocshArgString};
static const iocshArg phytronBrakeArg1 = {"Axis number", iocshArgInt};
static const iocshArg phytronBrakeArg2 = {"Brake IO number", iocshArgInt};
static const iocshArg * const phytronBrakeArgs[] = {&phytronBrakeArg0,
&phytronBrakeArg1,
&phytronBrakeArg2};
static const iocshFuncDef phytronBrakeDef = {"PhytronConfigureBrake", 3, phytronBrakeArgs};
static void phytronBrakeCallFunc(const iocshArgBuf *args)
{
PhytronConfBrake(args[0].sval, args[1].ival, args[2].ival);
}
static void PhytronRegister(void)
{
iocshRegister(&PhytronCreateControllerDef, PhytronCreateContollerCallFunc);
iocshRegister(&phytronBrakeDef, phytronBrakeCallFunc);
}
extern "C" {

View File

@ -5,14 +5,19 @@ USAGE... Motor driver support for the Phytron MCC-2 motor controller.
Mark Koennecke
September 2016
Updated to go through SINQAxis for -MsgTxt support
Added a selector to support multiple phytrons on a connection
Mark Koennecke, January 2019
*/
#include "asynMotorController.h"
#include "asynMotorAxis.h"
#include "SINQController.h"
#include "SINQAxis.h"
#define COMLEN 80
class PhytronAxis : public asynMotorAxis
class PhytronAxis : public SINQAxis
{
public:
/* These are the methods we override from the base class */
@ -25,6 +30,7 @@ public:
asynStatus poll(bool *moving);
asynStatus setPosition(double position);
asynStatus setClosedLoop(bool closedLoop);
int setBrake(int brakeIO);
private:
char phytronChar;
@ -32,15 +38,19 @@ private:
* Abbreviated because it is used very frequently */
double position;
int homing;
int homing_direction; /*1 forward, 0 backwards */
time_t next_poll;
int encoder;
int haveBrake;
int brakeIO;
friend class PhytronController;
};
class PhytronController : public asynMotorController {
class PhytronController : public SINQController {
public:
PhytronController(const char *portName, const char *PhytronPortName, int encX, int encY);
PhytronController(const char *portName, const char *PhytronPortName, const char *selector,
int encX, int encY);
void report(FILE *fp, int level);
PhytronAxis* getAxis(asynUser *pasynUser);
@ -50,7 +60,8 @@ friend class PhytronAxis;
private:
asynUser *pasynUserController_;
asynStatus transactController(char command[COMLEN], char reply[COMLEN]);
asynStatus transactController(int axisNo, char command[COMLEN], char reply[COMLEN]);
const char *selector;
};

View File

@ -12,6 +12,7 @@ SINQAxis::SINQAxis(class SINQController *pC, int axis)
: asynMotorAxis((asynMotorController *)pC, axis),
pC_(pC)
{
updateMsgTxtFromDriver("");
}
@ -22,6 +23,7 @@ void SINQAxis::updateMsgTxtFromDriver(const char *value)
setStringParam(pC_->motorMessageText_,value);
} else {
pC_->setIntegerParam(axisNo_,pC_->motorMessageIsFromDriver_, 0);
setStringParam(pC_->motorMessageText_,"");
}
}

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

@ -27,7 +27,7 @@
*
* Mark Koennecke, February 2013
*
* Enhanced by adding an addtional external pause and status flag in the databse and code in here
* Enhanced by adding an addtional external pause and status flag in the database and code in here
* to handle this. Moreover an external MsgTxt field in the DB can be filled with an error message.
*
* Mark Koennecke, July 2017
@ -163,7 +163,7 @@ static void connectSlaveRecords(EL737priv *priv)
errlogPrintf("dbNameToAddr failed for %s with %s\n", slaveName, errName);
priv->dbInit = 0;
} else {
errlogPrintf("dbNameToAddr succeded for %s, record access %s\n",slaveName, priv->status.precord->name);
errlogPrintf("dbNameToAddr succeded for %s, record access %s\n",slaveName, priv->threshCounter.precord->name);
}
snprintf(slaveName,sizeof(slaveName),"%s:Threshold", priv->psr->name);
@ -174,9 +174,9 @@ static void connectSlaveRecords(EL737priv *priv)
errlogPrintf("dbNameToAddr failed for %s with %s\n", slaveName, errName);
priv->dbInit = 0;
} else {
errlogPrintf("dbNameToAddr succeded for %s, record access %s\n",slaveName, priv->status.precord->name);
errlogPrintf("dbNameToAddr succeded for %s, record access %s\n",slaveName, priv->threshold.precord->name);
}
priv->thresholdValue = -999;
}
static long el737_init_record(scalerRecord *psr, CALLBACK *pcallback)
@ -225,14 +225,14 @@ static long el737_init_record(scalerRecord *psr, CALLBACK *pcallback)
status = pasynOctetSyncIO->connect(port, 0, &priv->asynContext, NULL);
if (status) {
asynPrint(dummyUser, ASYN_TRACE_ERROR,
"%s: cannot connect to EL734 controller\n",
"%s: cannot connect to EL737 controller\n",
"el737_init_scaler");
psr->pact = 1;
return 0;
}
pasynOctetSyncIO->setOutputEos(priv->asynContext,"\r",strlen("\r"));
pasynOctetSyncIO->setInputEos(priv->asynContext,"\r",strlen("\r"));
priv->asynContext->timeout = 1.0;
priv->asynContext->timeout = 2.0;
strcpy(command,"RMT 1");
pasynOctetSyncIO->writeRead(priv->asynContext, command, strlen(command),
reply,sizeof(reply), 1.,&out,&in,&reason);
@ -343,7 +343,7 @@ static asynStatus el737_transactCommand(EL737priv *priv,char command[COMLEN],cha
pasynOctetSyncIO->flush(priv->asynContext);
strcpy(message,"OK");
strcpy(message,"");
status = pasynOctetSyncIO->writeRead(priv->asynContext, command, strlen(command),
reply,COMLEN, 1.,&out,&in,&reason);
if(status == asynSuccess){
@ -414,7 +414,7 @@ static void runEvents(EL737priv *priv)
This is the better way to set the threshold rather then the old way below which uses
presets for that function. This one uses separate fields.
*/
if(priv->dbInit == 1) {
if(priv->dbInit == 1 && priv->sendThreshold) {
dbStatus = dbGetField(&priv->threshold,DBR_LONG,&myThreshold,&options, &nElements,NULL);
if(dbStatus != 0){
errSymLookup(dbStatus,errName,sizeof(errName));
@ -425,13 +425,16 @@ static void runEvents(EL737priv *priv)
we have to set the threshold
*/
dbStatus = dbGetField(&priv->threshCounter,DBR_LONG,&threshCounter, &options, &nElements,NULL);
if(dbStatus != 0){
if(dbStatus != 0) {
errSymLookup(dbStatus,errName,sizeof(errName));
errlogPrintf("Reading thresholdCounter failed with %s\n", errName);
} else {
if(threshCounter == 0){
threshCounter = 1;
}
sprintf(command,"DL %d %d", (int)threshCounter,
(int)myThreshold);
//errlogPrintf("Sending threshold command %s\n", command);
errlogPrintf("Sending threshold command %s\n", command);
status = el737_transactCommand(priv,command,reply);
sprintf(command,"DR %d", (int)threshCounter);
status = el737_transactCommand(priv, command,reply);
@ -442,17 +445,22 @@ static void runEvents(EL737priv *priv)
}
}
}
}
}
if(priv->sendThreshold == 1){
sprintf(command,"DL %d %d", (int)priv->presets[THRESHMON],
(int)priv->presets[THRESHVAL]);
//errlogPrintf("Sending threshold command %s\n", command);
status = el737_transactCommand(priv,command,reply);
sprintf(command,"DR %d", (int)priv->presets[THRESHMON]);
status = el737_transactCommand(priv, command,reply);
if(status == asynSuccess){
priv->sendThreshold = 0;
if(priv->sendThreshold == 1) {
// fallback when we do not have the DB fields or threshCounter is invalid
if(priv->presets[THRESHMON] <= 0) {
errlogPrintf("Invalid threshold preset monitor %ld, no threshold sent", priv->presets[THRESHMON]);
} else {
sprintf(command,"DL %d %d", (int)priv->presets[THRESHMON],
(int)priv->presets[THRESHVAL]);
errlogPrintf("Sending threshold from presets, command %s\n", command);
status = el737_transactCommand(priv,command,reply);
sprintf(command,"DR %d", (int)priv->presets[THRESHMON]);
status = el737_transactCommand(priv, command,reply);
if(status == asynSuccess) {
priv->sendThreshold = 0;
}
}
}
@ -542,7 +550,6 @@ static void el737Thread(void *param)
evStatus = epicsEventWaitWithTimeout(priv->wakeUp,timeout);
//errlogPrintf("EL737 thread woke up with %d\n", evStatus);
if(evStatus == epicsEventWaitOK) {
/*
FEAR: we received a command!!!!

File diff suppressed because it is too large Load Diff

View File

@ -7,21 +7,28 @@
* Matthew Pearson
* 23 May 2012
*
* Modified to use the MsgTxt field for SINQ
*
* Mark Koennecke, January 2019
*
* EXtended with special motor axis for the Selene
* guide, Mark Koennecke, February 2020
********************************************/
#ifndef pmacAxis_H
#define pmacAxis_H
#include "asynMotorController.h"
#include "asynMotorAxis.h"
#include "SINQController.h"
#include "SINQAxis.h"
class pmacController;
class SeleneController;
class pmacAxis : public asynMotorAxis
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);
@ -29,8 +36,9 @@ class pmacAxis : public asynMotorAxis
asynStatus stop(double acceleration);
asynStatus poll(bool *moving);
asynStatus setPosition(double position);
private:
asynStatus enable(int on);
protected:
pmacController *pC_;
asynStatus getAxisStatus(bool *moving);
@ -53,9 +61,100 @@ class pmacAxis : public asynMotorAxis
time_t status6Time;
int starting;
int homing;
double statusPos;
time_t next_poll;
bool autoEnable;
friend class pmacController;
friend class pmacV3Controller;
};
/*----------------------------------------------------------------------------------------------*/
class pmacHRPTAxis : public pmacAxis
{
public:
pmacHRPTAxis(pmacController *pController, int axisNo) : pmacAxis(pController,axisNo) {};
/**
* Override getAxisStatus in order to read the special parameter indicating a
* slit blade crash at HRPT
*/
asynStatus getAxisStatus(bool *moving);
protected:
friend class pmacController;
};
/*--------------------------------------------------------------------------------------------*/
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);
protected:
friend class SeleneController;
friend class pmacController;
private:
double limitTarget;
asynStatus getSeleneAxisInitialStatus(void);
};
/*
Yet another special set of motors for the Selene Guide at AMOR. Each segment can be lifted or tilted. This is
two motors. One acts as a slave and only writes a new target, the other also sets a new target and sends the
actual movement command. Both motors are coordianted in the motor controller in order to avoid tension on
the guide elements. This gaves rise to the function code LIFTSLAVE and LIFTMASTER.
In another mode the whole guide can be lifted or tilted. Then motor 1 and 6 get new values and one of them
sends the drive command. This causes all 6 motors to drive synchronously to their new targets. This is
implemented through the LIFTSEGMENT function code.
Mark Koennecke, February 2020
The axis should not be enabled automatically
Michele Brambilla, February 2020
*/
class LiftAxis : public pmacAxis
{
public:
LiftAxis(pmacController *pController, int axisNo) : pmacAxis((pmacController *)pController,axisNo) {};
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration);
asynStatus poll(bool *moving);
asynStatus stop(double acceleration);
private:
int waitStart;
};
/********************************************
* Protocol version 3 requires just some minor change
*
* Michele Brambilla, February 2022
********************************************/
class pmacV3Axis : public pmacAxis {
public:
pmacV3Axis(pmacController *pController, int axisNo);
asynStatus move(double position, int relative, double min_velocity,
double max_velocity, double acceleration);
asynStatus poll(bool *moving);
protected:
int IsEnable;
double Speed;
asynStatus getAxisStatus(bool *moving);
friend class pmacController;
friend class pmacV3Controller;
};
#endif /* pmacAxis_H */

View File

@ -13,6 +13,11 @@
* This affects also the commands they understand.
*
* Mark Koennecke, February 2013
*
* Updated to use the MsgTxt field for error messages as
* used at ESS and SINQ
*
* Mark Koennecke, January 2019
********************************************/
@ -27,17 +32,19 @@
using std::cout;
using std::endl;
#include <epicsTime.h>
#include "epicsTime.h"
#include <epicsThread.h>
#include <epicsExport.h>
#include <epicsString.h>
#include <iocsh.h>
#include <drvSup.h>
#include <registryFunction.h>
#include <errlog.h>
#include "asynOctetSyncIO.h"
#include "pmacController.h"
#include "pmacAxis.h"
#define MULT 1000.
@ -124,25 +131,23 @@ const epicsUInt32 pmacController::PMAX_AXIS_GENERAL_PROB2 = (PMAC_STATUS2_DESIRE
extern "C" {
asynStatus pmacCreateController(const char *portName, const char *lowLevelPortName, int lowLevelPortAddress,
int numAxes, int movingPollPeriod, int idlePollPeriod);
asynStatus SeleneCreateController(const char *portName, const char *lowLevelPortName, int lowLevelPortAddress,
int numAxes, int movingPollPeriod, int idlePollPeriod);
asynStatus pmacV3CreateController(const char *portName,
const char *lowLevelPortName,
int lowLevelPortAddress, int numAxes,
int movingPollPeriod, int idlePollPeriod);
asynStatus pmacCreateAxis(const char *pmacName, int axis);
asynStatus pmacCreateAxis(const char *pmacName, int numAxis);
}
pmacController::pmacController(const char *portName, const char *lowLevelPortName, int lowLevelPortAddress,
int numAxes, double movingPollPeriod, double idlePollPeriod)
: asynMotorController(portName, numAxes+1, NUM_MOTOR_DRIVER_PARAMS,
0, // No additional interfaces
0, // No addition interrupt interfaces
ASYN_CANBLOCK | ASYN_MULTIDEVICE,
1, // autoconnect
0, 0) // Default priority and stack size
int numAxes, double movingPollPeriod, double idlePollPeriod, const int& extraParams)
: SINQController(portName, lowLevelPortName, numAxes+1, extraParams)
{
static const char *functionName = "pmacController::pmacController";
printf(" Constructor: %s\n", functionName);
//Initialize non static data members
lowLevelPortUser_ = NULL;
debugFlag_ = 0;
@ -153,7 +158,7 @@ pmacController::pmacController(const char *portName, const char *lowLevelPortNam
createParam(PMAC_C_CommsErrorString, asynParamInt32, &PMAC_C_CommsError_);
// Connect our Asyn user to the low level port that is a parameter to this constructor
if (lowLevelPortConnect(lowLevelPortName, lowLevelPortAddress, &lowLevelPortUser_, "\006", "\r") != asynSuccess) {
if (lowLevelPortConnect(lowLevelPortName, lowLevelPortAddress, &lowLevelPortUser_, "\006", (char *)"\r") != asynSuccess) {
printf("%s: Failed to connect to low level asynOctetSyncIO port %s\n", functionName, lowLevelPortName);
setIntegerParam(PMAC_C_CommsError_, 1);
} else {
@ -251,7 +256,7 @@ asynStatus pmacController::printConnectedStatus()
* @param command - String command to send.
* @response response - String response back.
*/
asynStatus pmacController::lowLevelWriteRead(const char *command, char *response)
asynStatus pmacController::lowLevelWriteRead(int axisNo, const char *command, char *response)
{
asynStatus status = asynSuccess;
@ -260,6 +265,7 @@ asynStatus pmacController::lowLevelWriteRead(const char *command, char *response
size_t nread = 0;
int commsError = 0;
static const char *functionName = "pmacController::lowLevelWriteRead";
pmacAxis *axis = getAxis(axisNo);
debugFlow(functionName);
@ -286,6 +292,9 @@ asynStatus pmacController::lowLevelWriteRead(const char *command, char *response
if (status) {
asynPrint(lowLevelPortUser_, ASYN_TRACE_ERROR, "%s: Error from pasynOctetSyncIO->writeRead. command: %s\n", functionName, command);
setIntegerParam(this->motorStatusCommsError_, 1);
if(axis!= NULL){
axis->updateMsgTxtFromDriver("Lost connection to motor controller");
}
} else {
setIntegerParam(this->motorStatusCommsError_, 0);
}
@ -339,8 +348,6 @@ asynStatus pmacController::writeFloat64(asynUser *pasynUser, epicsFloat64 value)
pmacAxis *pAxis = NULL;
char command[64] = {0};
char response[64] = {0};
double encRatio = 1.0;
epicsInt32 encposition = 0;
char message[132];
static const char *functionName = "pmacController::writeFloat64";
@ -408,6 +415,7 @@ asynStatus pmacController::writeFloat64(asynUser *pasynUser, epicsFloat64 value)
// bool moving = true;
// pAxis->getAxisStatus(&moving);
// } else
/*
if (function == motorLowLimit_) {
sprintf(command, "I%d14=%d", pAxis->axisNo_, (int)(value/pAxis->scale_/MULT));
asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
@ -422,8 +430,9 @@ asynStatus pmacController::writeFloat64(asynUser *pasynUser, epicsFloat64 value)
//Execute the command.
if (command[0] != 0 && status == asynSuccess) {
status = lowLevelWriteRead(command, response);
status = lowLevelWriteRead(pAxis->axisNo_,command, response);
}
*/
//Call base class method
//This will handle callCallbacks even if the function was handled here.
@ -461,7 +470,6 @@ asynStatus pmacController::writeInt32(asynUser *pasynUser, epicsInt32 value)
}
/** Returns a pointer to an pmacAxis object.
* Returns NULL if the axis number encoded in pasynUser is invalid.
* \param[in] pasynUser asynUser structure that encodes the axis index number. */
@ -527,18 +535,92 @@ asynStatus pmacCreateController(const char *portName, const char *lowLevelPortNa
return asynSuccess;
}
asynStatus SeleneCreateController(const char *portName, const char *lowLevelPortName, int lowLevelPortAddress,
int numAxes, int movingPollPeriod, int idlePollPeriod)
{
SeleneController *ppmacController
= new SeleneController(portName, lowLevelPortName, lowLevelPortAddress, numAxes, movingPollPeriod/1000., idlePollPeriod/1000.);
ppmacController = NULL;
return asynSuccess;
}
asynStatus pmacV3CreateController(const char *portName, const char *lowLevelPortName, int lowLevelPortAddress,
int numAxes, int movingPollPeriod, int idlePollPeriod)
{
pmacController *ppmacController = new pmacV3Controller(portName, lowLevelPortName, lowLevelPortAddress, numAxes, movingPollPeriod/1000., idlePollPeriod/1000.);
ppmacController = NULL;
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();
}
pmacV3Controller::pmacV3Controller(const char *portName,
const char *lowLevelPortName,
int lowLevelPortAddress, int numAxes,
double movingPollPeriod,
double idlePollPeriod,
const int &extraParams) :
pmacController(portName, lowLevelPortName, lowLevelPortAddress, numAxes, movingPollPeriod, idlePollPeriod, extraParams) {
static const char *functionName = "pmacV3Controller::pmacV3Controller";
createParam(EnableAxisString, asynParamInt32, &enableAxis_);
createParam(AxisEnabledString, asynParamInt32, &axisEnabled_);
callParamCallbacks();
}
/**
* C wrapper for the pmacAxis constructor.
* See pmacAxis::pmacAxis.
*
*/
asynStatus pmacCreateAxis(
const char *pmacName, /* specify which controller by port name */
int axis) /* axis number (start from 1). */
{
pmacController *pC;
pmacAxis *pAxis;
static const char *functionName = "pmacCreateAxis";
pC = (pmacController *)findAsynPortDriver(pmacName);
if (!pC) {
printf("%s:%s: Error port %s not found\n", driverName, functionName,
pmacName);
return asynError;
}
pC->lock();
pAxis = new pmacAxis(pC, axis);
pAxis = NULL;
pC->unlock();
return asynSuccess;
}
/**
* C wrapper for the pmacAxis constructor.
* See pmacAxis::pmacAxis.
* C wrapper for the pmacHRPTAxis constructor.
* See pmacHRPTAxis::pmacHRPTAxis.
*
*/
asynStatus pmacCreateAxis(const char *pmacName, /* specify which controller by port name */
asynStatus pmacCreateHRPTAxis(const char *pmacName, /* specify which controller by port name */
int axis) /* axis number (start from 1). */
{
pmacController *pC;
pmacAxis *pAxis;
static const char *functionName = "pmacCreateAxis";
static const char *functionName = "pmacCreateHRPTAxis";
pC = (pmacController*) findAsynPortDriver(pmacName);
if (!pC) {
@ -548,14 +630,97 @@ asynStatus pmacCreateAxis(const char *pmacName, /* specify which control
}
pC->lock();
pAxis = new pmacAxis(pC, axis);
pAxis = new pmacHRPTAxis(pC, axis);
pAxis = NULL;
pC->unlock();
return asynSuccess;
}
/**
* C Wrapper function for pmacAxis constructor.
* C wrapper for the SeleneAxis constructor.
* See SeleneAxis::SeleneAxis.
*
*/
asynStatus SeleneCreateAxis(const char *pmacName, /* specify which controller by port name */
int axis, /* axis number (start from 1). */
double limitTarget)
{
SeleneController *pC;
SeleneAxis *pAxis;
static const char *functionName = "SeleneCreateAxis";
pC = (SeleneController*) findAsynPortDriver(pmacName);
if (!pC) {
printf("%s:%s: Error port %s not found\n",
driverName, functionName, pmacName);
return asynError;
}
pC->lock();
pAxis = new SeleneAxis(pC, axis, limitTarget);
pAxis = NULL;
pC->unlock();
return asynSuccess;
}
/**
* C wrapper for the Selene LiftAxis constructor.
* See LiftAxis::LiftAxis.
*
*/
asynStatus LiftCreateAxis(const char *pmacName, /* specify which controller by port name */
int axis) /* axis number (start from 1). */
{
pmacController *pC;
LiftAxis *pAxis;
static const char *functionName = "LiftCreateAxis";
pC = (pmacController*) findAsynPortDriver(pmacName);
if (!pC) {
printf("%s:%s: Error port %s not found\n",
driverName, functionName, pmacName);
return asynError;
}
pC->lock();
pAxis = new LiftAxis(pC, axis);
pAxis = NULL;
pC->unlock();
return asynSuccess;
}
/**
* C wrapper for the pmacV3Axis constructor.
* See pmacAxis::pmacV3Axis.
*
*/
asynStatus pmacV3CreateAxis(
const char *pmacName, /* specify which controller by port name */
int axis) /* axis number (start from 1). */
{
pmacController *pC;
pmacAxis *pAxis;
static const char *functionName = "pmacV3CreateAxis";
pC = (pmacController *)findAsynPortDriver(pmacName);
if (!pC) {
printf("%s:%s: Error port %s not found\n", driverName, functionName,
pmacName);
return asynError;
}
pC->lock();
pAxis = new pmacV3Axis(pC, axis);
pAxis = NULL;
pC->unlock();
return asynSuccess;
}
/**
* C Wrapper function for pmacHRPTAxis constructor.
* See pmacAxis::pmacAxis.
* This function allows creation of multiple pmacAxis objects with axis numbers 1 to numAxes.
* @param pmacName Asyn port name for the controller (const char *)
@ -586,6 +751,137 @@ asynStatus pmacCreateAxes(const char *pmacName,
return asynSuccess;
}
/*================================ SeleneController ===============================================*/
asynStatus SeleneController::writeFloat64(asynUser *pasynUser, epicsFloat64 value)
{
int function = pasynUser->reason;
asynStatus status = asynError;
SeleneAxis *pAxis = NULL;
char command[64] = {0};
char response[64] = {0};
char message[132];
static const char *functionName = "SeleneController::writeFloat64";
sprintf(message,"%s, reason %d", functionName, function);
pAxis = (SeleneAxis *)this->getAxis(pasynUser);
if (!pAxis) {
return asynError;
}
/* Set the parameter and readback in the parameter library. */
status = pAxis->setDoubleParam(function, value);
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);
errlogPrintf("Setting low limit of axis %d to %f, command = %s\n", pAxis->axisNo_, value, command);
} else if (function == motorHighLimit_) {
sprintf(command, "Q%d53=%f", pAxis->axisNo_, (float)value/(float)MULT);
asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
"%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) {
status = lowLevelWriteRead(pAxis->axisNo_,command, response);
}
// What if now status != asynSuccess
//Call base class method
//This will handle callCallbacks even if the function was handled here.
status = asynMotorController::writeFloat64(pasynUser, value);
return status;
}
asynStatus pmacV3Controller::writeInt32(asynUser *pasynUser, epicsInt32 value) {
int function = pasynUser->reason;
asynStatus status = asynSuccess;
pmacV3Axis *pAxis = NULL;
char command[64] = {0};
char response[64] = {0};
static const char *functionName = "pmacV3Controller::writeInt32";
debugFlow(functionName);
pAxis = (pmacV3Axis*)this->getAxis(pasynUser);
if (!pAxis) {
return asynError;
}
/* Set the parameter and readback in the parameter library. This may be
* overwritten when we read back the status at the end, but that's OK */
pAxis->setIntegerParam(function, value);
if (function == enableAxis_) {
if(value == 1) {
sprintf(command, "M%2.2d=15\n", pAxis->axisNo_);
lowLevelWriteRead(pAxis->axisNo_, command, response);
}
sprintf(command, "M%2.2d14=%d\n", pAxis->axisNo_, value);
asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
"%s: Enable axis on controller %s, axis %d enable=%d\n",
functionName, portName, pAxis->axisNo_, value);
if(value){
pAxis->updateMsgTxtFromDriver("");
}
}
// Execute the command.
if (command[0] != 0 && status == asynSuccess) {
lowLevelWriteRead(pAxis->axisNo_, command, response);
}
return pmacController::writeInt32(pasynUser, value);
}
asynStatus pmacV3Controller::readInt32(asynUser *pasynUser, epicsInt32 *value) {
int function = pasynUser->reason;
asynStatus status = asynError;
pmacV3Axis *pAxis = NULL;
static const char *functionName = "pmacV3Controller::readInt32";
char command[128];
char response[128];
debugFlow(functionName);
pAxis = (pmacV3Axis*)(this->getAxis(pasynUser));
if (!pAxis) {
return asynError;
}
if (function == axisEnabled_) {
snprintf(command, sizeof(command), "P%2.2d00", pAxis->axisNo_);
status = this->lowLevelWriteRead(pAxis->axisNo_, command, response);
*value = strtol(response, NULL, 10);
int st = setIntegerParam(axisEnabled_, *value);
callParamCallbacks();
}
return pmacController::readInt32(pasynUser, value);
}
/* Code for iocsh registration */
#ifdef vxWorks
@ -610,6 +906,18 @@ static void configpmacCreateControllerCallFunc(const iocshArgBuf *args)
pmacCreateController(args[0].sval, args[1].sval, args[2].ival, args[3].ival, args[4].ival, args[5].ival);
}
static const iocshFuncDef configSeleneCreateController = {"SeleneCreateController", 6, pmacCreateControllerArgs};
static void configSeleneCreateControllerCallFunc(const iocshArgBuf *args)
{
SeleneCreateController(args[0].sval, args[1].sval, args[2].ival, args[3].ival, args[4].ival, args[5].ival);
}
static const iocshFuncDef configpmacV3CreateController = {
"pmacV3CreateController", 6, pmacCreateControllerArgs
};
static void configpmacV3CreateControllerCallFunc(const iocshArgBuf *args) {
pmacV3CreateController(args[0].sval, args[1].sval, args[2].ival, args[3].ival,
args[4].ival, args[5].ival);
}
/* pmacCreateAxis */
static const iocshArg pmacCreateAxisArg0 = {"Controller port name", iocshArgString};
@ -623,6 +931,58 @@ static void configpmacAxisCallFunc(const iocshArgBuf *args)
pmacCreateAxis(args[0].sval, args[1].ival);
}
/* pmacCreateHRPTAxis */
static const iocshArg pmacCreateHRPTAxisArg0 = {"Controller port name", iocshArgString};
static const iocshArg pmacCreateHRPTAxisArg1 = {"Axis number", iocshArgInt};
static const iocshArg * const pmacCreateHRPTAxisArgs[] = {&pmacCreateAxisArg0,
&pmacCreateAxisArg1};
static const iocshFuncDef configpmacHRPTAxis = {"pmacCreateHRPTAxis", 2, pmacCreateHRPTAxisArgs};
static void configpmacHRPTAxisCallFunc(const iocshArgBuf *args)
{
pmacCreateHRPTAxis(args[0].sval, args[1].ival);
}
/* SeleneCreateAxis */
static const iocshArg SeleneCreateAxisArg0 = {"Controller port name", iocshArgString};
static const iocshArg SeleneCreateAxisArg1 = {"Axis number", iocshArgInt};
static const iocshArg SeleneCreateAxisArg2 = {"limitTraget", iocshArgDouble};
static const iocshArg * const SeleneCreateAxisArgs[] = {&SeleneCreateAxisArg0,
&SeleneCreateAxisArg1,
&SeleneCreateAxisArg2};
static const iocshFuncDef configSeleneCreateAxis = {"SeleneCreateAxis", 3, SeleneCreateAxisArgs};
static void configSeleneCreateAxisCallFunc(const iocshArgBuf *args)
{
SeleneCreateAxis(args[0].sval, args[1].ival,args[2].dval);
}
/* LiftCreateAxis */
static const iocshArg LiftCreateAxisArg0 = {"Controller port name", iocshArgString};
static const iocshArg LiftCreateAxisArg1 = {"Axis number", iocshArgInt};
static const iocshArg * const LiftCreateAxisArgs[] = {&LiftCreateAxisArg0,
&LiftCreateAxisArg1};
static const iocshFuncDef configLiftAxis = {"LiftCreateAxis", 2, LiftCreateAxisArgs};
static void configLiftAxisCallFunc(const iocshArgBuf *args)
{
LiftCreateAxis(args[0].sval, args[1].ival);
}
/* pmacV3CreateAxis */
static const iocshArg pmacV3CreateAxisArg0 = {"Controller port name",
iocshArgString};
static const iocshArg pmacV3CreateAxisArg1 = {"Axis number", iocshArgInt};
static const iocshArg *const pmacV3CreateAxisArgs[] = {&pmacV3CreateAxisArg0,
&pmacV3CreateAxisArg1};
static const iocshFuncDef configpmacV3Axis = {"pmacV3CreateAxis", 2,
pmacV3CreateAxisArgs};
static void configpmacV3AxisCallFunc(const iocshArgBuf *args) {
pmacV3CreateAxis(args[0].sval, args[1].ival);
}
/* pmacCreateAxes */
static const iocshArg pmacCreateAxesArg0 = {"Controller port name", iocshArgString};
static const iocshArg pmacCreateAxesArg1 = {"Num Axes", iocshArgInt};
@ -640,7 +1000,13 @@ static void configpmacAxesCallFunc(const iocshArgBuf *args)
static void pmacControllerRegister(void)
{
iocshRegister(&configpmacCreateController, configpmacCreateControllerCallFunc);
iocshRegister(&configSeleneCreateController, configSeleneCreateControllerCallFunc);
iocshRegister(&configpmacV3CreateController, configpmacV3CreateControllerCallFunc);
iocshRegister(&configpmacAxis, configpmacAxisCallFunc);
iocshRegister(&configpmacHRPTAxis, configpmacHRPTAxisCallFunc);
iocshRegister(&configSeleneCreateAxis, configSeleneCreateAxisCallFunc);
iocshRegister(&configLiftAxis, configLiftAxisCallFunc);
iocshRegister(&configpmacV3Axis, configpmacV3AxisCallFunc);
iocshRegister(&configpmacAxes, configpmacAxesCallFunc);
}
epicsExportRegistrar(pmacControllerRegister);
@ -649,3 +1015,4 @@ epicsExportRegistrar(pmacControllerRegister);
} // extern "C"

View File

@ -7,23 +7,27 @@
* Matthew Pearson
* 23 May 2012
*
*
* Modified to use the MsgTxt field for SINQ
*
* Mark Koennecke, January 2019
********************************************/
#ifndef pmacController_H
#define pmacController_H
#include "asynMotorController.h"
#include "SINQController.h"
#include "asynMotorAxis.h"
#include "pmacAxis.h"
#define PMAC_C_GlobalStatusString "PMAC_C_GLOBALSTATUS"
#define PMAC_C_CommsErrorString "PMAC_C_COMMSERROR"
class pmacController : public asynMotorController {
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();
@ -45,13 +49,13 @@ class pmacController : public asynMotorController {
int PMAC_C_GlobalStatus_;
int PMAC_C_CommsError_;
#define LAST_PMAC_PARAM PMAC_C_CommsError__
void debugFlow(const char *message);
asynStatus lowLevelWriteRead(int axisNo, const char *command, char *response);
private:
asynUser* lowLevelPortUser_;
epicsUInt32 debugFlag_;
asynStatus lowLevelWriteRead(const char *command, char *response);
int lowLevelPortConnect(const char *port, int addr, asynUser **ppasynUser, char *inputEos, char *outputEos);
void debugFlow(const char *message);
//static class data members
@ -132,9 +136,57 @@ class pmacController : public asynMotorController {
static const epicsUInt32 PMAX_AXIS_GENERAL_PROB2;
friend class pmacAxis;
friend class pmacHRPTAxis;
friend class SeleneAxis;
friend class LiftAxis;
friend class pmacV3Axis;
};
#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);
~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_;
};
#define EnableAxisString "ENABLE_AXIS"
#define AxisEnabledString "AXIS_ENABLED"
class pmacV3Controller : public pmacController {
public:
pmacV3Controller(const char *portName, const char *lowLevelPortName,
int lowLevelPortAddress, int numAxes, double movingPollPeriod,
double idlePollPeriod, const int &extraParams = 2);
// overloaded because we want to enable/disable the motor
asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
// overloaded because we want to read the axis state
asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value);
friend class pmacV3Axis;
friend class pmacAxis;
protected:
pmacV3Axis **pAxes_; /**< Array of pointers to axis objects */
int enableAxis_;
int axisEnabled_;
};
#endif /* pmacController_H */

View File

@ -3,8 +3,10 @@
#---------------------------------------------
registrar(EL734Register)
registrar(PhytronRegister)
registrar(EuroMoveRegister)
registrar(NanotecRegister)
registrar(pmacControllerRegister)
registrar(pmacAsynIPPortRegister)
#--------------------------------------------------------
# With the PSI module build system, including these items actually

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)