67 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
7023f52336 Modified devScalerEL737 to process threshold and thresholdcounter as fields external to
the scaler record. This is to replace the preset hack previously used to implement this
functionality.
2017-08-21 16:30:15 +02:00
83a995a16d Fixed the value reading bug in devScalerEL737 (wrong function signature)
Nihkil added mor error codes to devScalerEL737
2017-08-21 14:42:09 +02:00
124aa97cd6 Fixed a bug in el737_read which had a wrong function signature. May have changed over the years.
This caused every second monitor to be 0. A number type mismatch.
2017-08-14 14:52:04 +02:00
bd3b808312 Commented the various Makefiles to say what they are for. 2017-08-09 16:51:38 +02:00
9a3b0f7666 Improved error reporting to the MsgTxt additional field 2017-08-08 17:32:32 +02:00
0eed0c9357 Fixed bad logic in devScalerEL7373.c:connectSlaveRecords() which caused a core dump when accessing non existing
slave records
2017-07-25 08:01:27 +02:00
7263a0772f Added support for an addition Pause, Status and MsgTxt field to the EL737 scaler driver 2017-07-24 09:45:57 +02:00
9c507a05ba Merge branch 'errtxt'
Also made sinqepicsApp work with the module based build system now being operational at PSI
Conflicts:
	iocBoot/iocsinqEPICS/envPaths
	iocBoot/iocsinqEPICS/st.cmd
	sinqEPICSApp/src/Makefile
2017-07-04 08:38:40 +02:00
dc966bc976 Merge branch 'ess' 2017-05-17 11:23:50 +02:00
edd3005f85 Merge branch 'phytron' 2017-05-17 11:22:43 +02:00
af4d16d25f Implemented a feature which allows for an additional field for having a text message
with a motor axis. To this purpose a new derived class SINQAxis and SINQController were
added which other drivers using this feature can derive from. The proof of concept and
test platform is the EL734 driver.
2017-03-20 16:40:25 +01:00
6ea6fdcb85 I added a driver for the EL755 magnet controller using streamdevice 2017-03-07 16:37:19 +01:00
389e7f9157 The ESS module version is now working!
References DM-89
2017-02-17 11:09:23 +01:00
8858761e74 I now have a ESS style module Makefile.EEE which compiles.... I just do not yet know if it
works.....
2017-02-14 17:21:39 +01:00
2950f21353 Added support for the dimetix distance measurement device
Refs #DM-93
2016-08-09 15:36:22 +02:00
28999f2212 Added support for the AMOR SPS-S5 using streamdevice 2016-08-04 14:38:50 +02:00
7c1693e606 Added support for the AMOR SLS VME Magnet controllers to the sinq IOC application
refs #DM-91
2016-07-08 13:53:21 +02:00
c75957f284 The SINQ IOC working in the ESS EPICS environment 2016-06-16 15:20:55 +02:00
8f0c240e76 Removed unnecessary stuff from the Makefile which was introduced while trying to get this to compile 2016-06-03 16:54:30 +02:00
5dff3bc23b First compile on the ESS EPICS environment. Success for the motor code.
Still missing:
   * scaler support
   * cleanup of Makefile
2016-06-03 16:52:02 +02:00
63 changed files with 4391 additions and 638 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

@ -1,3 +1,4 @@
# This is for building the IOC application against the EEE-setup
#Makefile at top of application tree
TOP = .
include $(TOP)/configure/CONFIG

33
Makefile.EEE Normal file
View File

@ -0,0 +1,33 @@
# This build the SINQ stuff as an EEE module. Which we cannot use yet because of the political problem
# of integrating this module into the EEE distribution
include ${EPICS_ENV_PATH}/module.Makefile
PROJECT=sinq
USR_DEPENDENCIES = asyn,4.27.0
USR_DEPENDENCIES += motor,6.10.0
USR_DEPENDENCIES += synAppsStd,3.4.1
USR_DEPENDENCIES += streamdevice,2.6.0
USR_DEPENDENCIES += busy,1.6.0
USR_DEPENDENCIES += pcre,8.36.0
TEMPLATES += sinqEPICSApp/Db/dimetix.db
TEMPLATES += sinqEPICSApp/Db/slsvme.db
TEMPLATES += sinqEPICSApp/Db/spsamor.db
TEMPLATES += sinqEPICSApp/Db/el737Record.db
DBDS += sinqEPICSApp/src/sinq.dbd
SUBSTITUTIONS=-none-
OPIS=-none-
# What we need at SINQ
SOURCES += sinqEPICSApp/src/devScalerEL737.c
SOURCES += sinqEPICSApp/src/EL734Driver.cpp
SOURCES += sinqEPICSApp/src/NanotecDriver.cpp
SOURCES += sinqEPICSApp/src/stptok.cpp
# MISCS would be the place to keep the stream device template files

36
Makefile.RHEL7 Normal file
View File

@ -0,0 +1,36 @@
# This build the sinq extensions for the PSI EPICS setup
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
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

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

@ -32,3 +32,5 @@ INSTALL_LOCATION=/afs/psi.ch/project/sinqdev/sinqepicsapp
# You must rebuild in the iocBoot directory for this to
# take effect.
#IOCS_APPL_TOP = </IOC/path/to/application/top>
STATIC_BUILD=NO
SHARED_LIBRARIES=YES

View File

@ -25,16 +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=/usr/local/epics
#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=/usr/local/epics/support/motor-6-7
ASYN=/usr/local/epics/support/asyn-4-18
STD=/usr/local/epics/support/std-3-1
ANC=/usr/local/epics/anc350v17
STREAMS=/usr/local/epics/support/StreamDevice-2-6
LAKESHORE336=/usr/local/epics/support/lakeshore336
BUSY=/usr/local/epics/support/busy-1-4
OXINSTCRYOJET=/usr/local/epics/support/OxInstCryojet-2-18-3
##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

61
essst.cmd Executable file
View File

@ -0,0 +1,61 @@
###!/opt/epics/modules/environment/1.8.0/3.14.12.5/bin/centos7-x86_64/iocsh
var requireDebug 2
require sinq, local
#---------- connect to controllers
drvAsynIPPortConfigure("serial1", "localhost:60001",0,0,0)
drvAsynIPPortConfigure("serial2", "localhost:60002",0,0,0)
drvAsynIPPortConfigure("serial3", "localhost:60003",0,0,0)
EL734CreateController("mota","serial1",12);
EL734CreateController("motb","serial2",12);
EL734CreateController("motc","serial3",12);
### Motors
dbLoadRecords("asynRecord.db","P=SQ:AMOR:,R=serial1,PORT=serial1,ADDR=0,OMAX=80,IMAX=80")
dbLoadRecords("asynRecord.db","P=SQ:AMOR:,R=serial2,PORT=serial1,ADDR=0,OMAX=80,IMAX=80")
dbLoadRecords("asynRecord.db","P=SQ:AMOR:,R=serial3,PORT=serial1,ADDR=0,OMAX=80,IMAX=80")
dbLoadTemplate "mota.substitutions"
dbLoadTemplate "motb.substitutions"
dbLoadTemplate "motc.substitutions"
#--------- load EL737 counter box
drvAsynIPPortConfigure("cter1","localhost:62000",0,0,0)
dbLoadRecords("asynRecord.db","P=SQ:AMOR:,R=cter1,PORT=cter1,ADDR=0,OMAX=80,IMAX=80")
dbLoadRecords("el737Record.db")
#asynSetTraceIOMask("cter1",0,2)
#----------- load Magnets
drvAsynIPPortConfigure("slsvme", "localhost:60066",0,0,0)
#drvAsynIPPortConfigure("slsvme", "localhost:8080",0,0,0)
dbLoadRecords("asynRecord.db","P=SQ:AMOR:,R=slsvme,PORT=slsvme,ADDR=0,OMAX=80,IMAX=80")
epicsEnvSet ("STREAM_PROTOCOL_PATH", "$(TOP)/db:.")
dbLoadRecords("slsvme.db","PREFIX=SQ:AMOR:PBY:,NO=1")
dbLoadRecords("slsvme.db","PREFIX=SQ:AMOR:FMA:,NO=2")
dbLoadRecords("slsvme.db","PREFIX=SQ:AMOR:ABY:,NO=3")
#-------------- load SPS
drvAsynIPPortConfigure("sps1", "localhost:60077",0,0,0)
dbLoadRecords("asynRecord.db","P=SQ:AMOR:,R=spsdirect,PORT=sps1,ADDR=0,OMAX=80,IMAX=80")
dbLoadRecords("spsamor.db","PREFIX=SQ:AMOR:SPS1:")
#------------- Load dimetix distance measurement device
drvAsynIPPortConfigure("dimetix", "localhost:60088",0,0,0)
dbLoadRecords("asynRecord.db","P=SQ:AMOR:,R=dimetixdirect,PORT=dimetix,ADDR=0,OMAX=80,IMAX=80")
dbLoadRecords("dimetix.db","PREFIX=SQ:AMOR:DIMETIX:")
iocInit
## Start any sequence programs
#seq sncxxx,"user=koenneckeHost"

View File

@ -1,8 +0,0 @@
epicsEnvSet("ARCH","linux-x86-debug")
epicsEnvSet("IOC","iocsinqEPICS")
epicsEnvSet("TOP","/afs/psi.ch/project/sinqdev/sinqepicsapp")
epicsEnvSet("EPICS_BASE","/usr/local/epics")
epicsEnvSet("ASYN","/usr/local/epics/support/asyn-4-18")
epicsEnvSet("MOTOR","/usr/local/epics/support/motor-6-7")
epicsEnvSet("LAKESHORE336","/usr/local/epics/support/lakeshore336")
epicsEnvSet("OXINSTCRYOJET","/usr/local/epics/support/OxInstCryojet-2-18-3")

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,10 +1,21 @@
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}
{P, N, M, DTYP, PORT, ADDR, DESC, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, MRES, PREC, DHLM, DLLM, INIT}
{NZ:mota:, 1, "m$(N)", "asynMotor", mota, 1, "sgu", degree, Pos, 2.0, 0.1, .2, 0, 1, .2, 1., 3, 20, -20, ""}
{NZ:mota:, 2, "m$(N)", "asynMotor", mota, 2, "sgl", degree, Pos, 2.0, 0.1, .2, 0, 1, .2, 1., 3, 20, -20, ""}
{NZ:mota:, 3, "m$(N)", "asynMotor", mota, 3, "sgd", degree, Pos, 2.0, 0.1, .2, 0, 1, .2, 1., 3, 20, -20, ""}
{NZ:mota:, 4, "m$(N)", "asynMotor", mota, 4, "som", degree, Pos, 2.0, 0.1, .2, 0, 1, .2, 0.001, 3, 37, -37, ""}
{NZ:mota:, 5, "m$(N)", "asynMotor", mota, 5, "sty", degree, Pos, 2.0, 0.1, .2, 0, 1, .2, 0.001, 3, 150, -149, ""}
}
file "$(SINQ)/Db/motorMessage.db"
{
pattern
{P,N, M,PORT}
{NZ:mota:, 1, "m$(N)",mota}
{NZ:mota:, 2, "m$(N)",mota}
{NZ:mota:, 3, "m$(N)",mota}
{NZ:mota:, 4, "m$(N)",mota}
{NZ:mota:, 5, "m$(N)",mota}
}

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

@ -1,41 +1,42 @@
#!../../bin/linux-x86/sinqEPICS
#!../../bin/linux-x86-debug/sinqEPICS
## You may have to change sinqEPICS to something else
## everywhere it appears in this file
cd /opt/amor/epics
< envPaths
cd ${TOP}
## Register all support components
dbLoadDatabase "dbd/sinqEPICS.dbd"
dbLoadDatabase "dbd/sinq.dbd"
sinqEPICS_registerRecordDeviceDriver pdbbase
## Load record instances
#dbLoadRecords("db/xxx.db","user=koenneckeHost")
#---------- load EL734 motor controller
drvAsynIPPortConfigure("serial1", "narziss-ts:3002",0,0,0)
#drvAsynIPPortConfigure("serial1", "localhost:8080",0,0,0)
EL734CreateController("mota","serial1",6);
#---------- connect to controllers
drvAsynIPPortConfigure("serial1", "localhost:60001",0,0,0)
drvAsynIPPortConfigure("serial2", "localhost:60002",0,0,0)
drvAsynIPPortConfigure("serial3", "localhost:60003",0,0,0)
EL734CreateController("mota","serial1",12);
EL734CreateController("motb","serial2",12);
EL734CreateController("motc","serial3",12);
### Motors
dbLoadRecords("$(ASYN)/db/asynRecord.db","P=NZ:,R=serial1,PORT=serial1,ADDR=0,OMAX=80,IMAX=80")
dbLoadRecords("$(ASYN)/db/asynRecord.db","P=SQ:AMOR:,R=serial1,PORT=serial1,ADDR=0,OMAX=80,IMAX=80")
dbLoadRecords("$(ASYN)/db/asynRecord.db","P=SQ:AMOR:,R=serial2,PORT=serial1,ADDR=0,OMAX=80,IMAX=80")
dbLoadRecords("$(ASYN)/db/asynRecord.db","P=SQ:AMOR:,R=serial3,PORT=serial1,ADDR=0,OMAX=80,IMAX=80")
cd ${TOP}/iocBoot/${IOC}
dbLoadTemplate "motor.substitutions.el734"
dbLoadTemplate "mota.substitutions"
dbLoadTemplate "motb.substitutions"
dbLoadTemplate "motc.substitutions"
#--------- load EL737 counter box
drvAsynIPPortConfigure("cter1","narziss-ts:3003",0,0,0)
dbLoadRecords("$(ASYN)/db/asynRecord.db","P=NZ:,R=cter1,PORT=cter1,ADDR=0,OMAX=80,IMAX=80")
drvAsynIPPortConfigure("cter1","localhost:62000",0,0,0)
dbLoadRecords("$(ASYN)/db/asynRecord.db","P=SQ:AMOR:,R=cter1,PORT=cter1,ADDR=0,OMAX=80,IMAX=80")
dbLoadRecords("${TOP}/db/el737Record.db")
asynSetTraceIOMask("cter1",0,2)
#asynSetTraceIOMask("cter1",0,2)
iocInit

View File

@ -0,0 +1,34 @@
#!../../bin/centos7-x86_64/sinqEPICS
# for debugging..................
## You may have to change sinqEPICS to something else
## everywhere it appears in this file
< envPaths
cd ${TOP}
## Register all support components
dbLoadDatabase "dbd/sinqEPICS.dbd"
dbLoadDatabase "dbd/sinq.dbd"
sinqEPICS_registerRecordDeviceDriver pdbbase
## Load record instances
#dbLoadRecords("db/xxx.db","user=koenneckeHost")
#---------- Install SPS
drvAsynIPPortConfigure("dimetix", "localhost:64000",0,0,0)
#drvAsynIPPortConfigure("slsvme", "localhost:8080",0,0,0)
dbLoadRecords("$(ASYN)/db/asynRecord.db","P=SQ:AMOR:,R=dimetix,PORT=dimetix,ADDR=0,OMAX=80,IMAX=80")
epicsEnvSet ("STREAM_PROTOCOL_PATH", "$(TOP)/sinqEPICSApp/Db:.")
cd ${TOP}/iocBoot/iocsinqEPICS
dbLoadRecords("$(TOP)/sinqEPICSApp/Db/dimetix.db","PREFIX=SQ:AMOR:DIMETIX:")
iocInit
## Start any sequence programs
#seq sncxxx,"user=koenneckeHost"

View File

@ -0,0 +1,34 @@
#!../../bin/linux-x86-debug/sinqEPICS
# for debugging..................
## You may have to change sinqEPICS to something else
## everywhere it appears in this file
< envPaths
cd ${TOP}
## Register all support components
dbLoadDatabase "$(TOP)/dbd/sinqEPICS.dbd"
dbLoadDatabase "$(TOP)/sinqEPICSApp/src/sinq.dbd"
sinqEPICS_registerRecordDeviceDriver pdbbase
## Load record instances
#dbLoadRecords("db/xxx.db","user=koenneckeHost")
#---------- Install SLSVME
drvAsynIPPortConfigure("el755", "narziss-ts:3004",0,0,0)
#drvAsynIPPortConfigure("slsvme", "localhost:8080",0,0,0)
dbLoadRecords("$(ASYN)/db/asynRecord.db","P=SQ:NZ:,R=el755,PORT=el755,ADDR=0,OMAX=80,IMAX=80")
epicsEnvSet ("STREAM_PROTOCOL_PATH", "$(TOP)/sinqEPICSApp/Db:.")
cd ${TOP}/iocBoot/iocsinqEPICS
dbLoadRecords("$(TOP)/sinqEPICSApp/Db/el755.db","PREFIX=SQ:NZ:AMAG:,NO=2")
iocInit
## Start any sequence programs
#seq sncxxx,"user=koenneckeHost"

View File

@ -0,0 +1,34 @@
#!../../bin/centos7-x86_64/sinqEPICS
# for debugging..................
## You may have to change sinqEPICS to something else
## everywhere it appears in this file
< envPaths
cd ${TOP}
## Register all support components
dbLoadDatabase "dbd/sinqEPICS.dbd"
dbLoadDatabase "dbd/sinq.dbd"
sinqEPICS_registerRecordDeviceDriver pdbbase
## Load record instances
#dbLoadRecords("db/xxx.db","user=koenneckeHost")
#---------- Install SLSVME
drvAsynIPPortConfigure("slsvme", "mpc2084:60066",0,0,0)
#drvAsynIPPortConfigure("slsvme", "localhost:8080",0,0,0)
dbLoadRecords("$(ASYN)/db/asynRecord.db","P=SQ:AMOR:,R=slsvme,PORT=slsvme,ADDR=0,OMAX=80,IMAX=80")
epicsEnvSet ("STREAM_PROTOCOL_PATH", "$(TOP)/sinqEPICSApp/Db:.")
cd ${TOP}/iocBoot/iocsinqEPICS
dbLoadRecords("slsvme.db","PREFIX=SQ:AMOR:PBY:,NO=1")
iocInit
## Start any sequence programs
#seq sncxxx,"user=koenneckeHost"

34
iocBoot/iocsinqEPICS/stsps.cmd Executable file
View File

@ -0,0 +1,34 @@
#!../../bin/centos7-x86_64/sinqEPICS
# for debugging..................
## You may have to change sinqEPICS to something else
## everywhere it appears in this file
< envPaths
cd ${TOP}
## Register all support components
dbLoadDatabase "dbd/sinqEPICS.dbd"
dbLoadDatabase "dbd/sinq.dbd"
sinqEPICS_registerRecordDeviceDriver pdbbase
## Load record instances
#dbLoadRecords("db/xxx.db","user=koenneckeHost")
#---------- Install SPS
drvAsynIPPortConfigure("spss5", "localhost:63000",0,0,0)
#drvAsynIPPortConfigure("slsvme", "localhost:8080",0,0,0)
dbLoadRecords("$(ASYN)/db/asynRecord.db","P=SQ:AMOR:,R=spss5,PORT=spss5,ADDR=0,OMAX=80,IMAX=80")
epicsEnvSet ("STREAM_PROTOCOL_PATH", "$(TOP)/sinqEPICSApp/Db:.")
cd ${TOP}/iocBoot/iocsinqEPICS
dbLoadRecords("$(TOP)/sinqEPICSApp/Db/spsamor.db","PREFIX=SQ:AMOR:SPSS5:")
iocInit
## Start any sequence programs
#seq sncxxx,"user=koenneckeHost"

7
makesinqtar Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/tclsh
set dir [pwd]
cd /usr/local/ioc
exec /usr/bin/tar czvf $dir/sinqepics.tgz modules/scaler modules/sinq modules/anc350 modules/autosave

View File

@ -10,7 +10,7 @@ include $(TOP)/configure/CONFIG
#----------------------------------------------------
# Create and install (or just install) into <top>/db
# databases, templates, substitutions like this
#DB += xxx.db
DB_INSTALL += slsvme.proto
#----------------------------------------------------
# If <anyname>.db template is not named <anyname>*.template add

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

@ -0,0 +1,40 @@
# DB file for dimetix laser distance measurement device
##
## Switch the laser
##
record(bo, "$(PREFIX)LASER") {
field(DESC, "Switch laser on and off")
field(DTYP,"stream")
field(OUT,"@dimetix.proto setlaser() dimetix 0")
field(PINI, "YES")
field(VAL, "0")
field(ZNAM, "OFF")
field(ONAM, "ON")
}
##
## Read the distance
##
record(ai, "$(PREFIX)DIST") {
field(DTYP, "stream")
field(DESC, "Distance")
field(INP, "@dimetix.proto read() dimetix 0")
field(SCAN, "1 second")
field(PREC, "1")
field(EGU, "mm")
}
##
## Set the readback (for simulation purposes only)
##
record(ao, "$(PREFIX)SimVal") {
field(DTYP, "stream")
field(DESC, "Set readback for simulation")
field(OUT, "@dimetix.proto setreadback() dimetix 0")
field(PREC, "1")
field(EGU, "mm")
}

View File

@ -0,0 +1,23 @@
# Streamdevice protocol file for a Dimetix distance measuring device as used at
# AMOR
InTerminator = "\n";
OutTerminator = "\r\n";
ExtraInput = Ignore;
setlaser {
out "%{s0p|s0o}";
in "g0?";
}
setreadback {
out "setval %d";
in "g0?";
}
read {
out "s0g";
in "g0g+%d";
@mismatch {in "@E213"}
}

View File

@ -0,0 +1,27 @@
record(bo,"$(P):Pause")
{
field(DTYP,"Soft Channel")
}
record(longin,"$(P):Status")
{
field(DTYP,"Soft Channel")
}
record(stringin,"$(P):MsgTxt")
{
field(DTYP,"Soft Channel")
}
record(longout,"$(P):ThresholdCounter")
{
field(DTYP,"Soft Channel")
}
record(longout,"$(P):Threshold")
{
field(DTYP,"Soft Channel")
}
record(scaler,"$(P)")
{
field(DESC,"$(DESC)")
field(DTYP,"asynScalerEL737")
field(OUT,"INST_IO @asyn($(PORT),0)")
}

22
sinqEPICSApp/Db/el755.db Normal file
View File

@ -0,0 +1,22 @@
##
## Read the Current
##
record(ai, "$(PREFIX)CurRBV") {
field(DTYP, "stream")
field(DESC, "Current Readback")
field(INP, "@el755.proto read($(NO)) el755 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, "@el755.proto write($(NO)) el755 0")
field(PREC, "3")
field(EGU, "A")
}

View File

@ -0,0 +1,22 @@
# Streamdevice protocol file for the PSI EL755 Magnet Controller
InTerminator = "\r";
OutTerminator = "\r";
#ExtraInput = Ignore;
read {
out "I \$1";
in "%*f %f";
@mismatch {out "RMT 1";
in "";
out "ECHO 0";
in "";
}
}
write {
out "I \$1 %f";
in "";
}

View File

@ -0,0 +1,18 @@
# The message text
record(waveform, "$(P)$(M)-MsgTxt") {
field(DTYP, "asynOctetRead")
field(INP, "@asyn($(PORT),$(N),1) MOTOR_MESSAGE_TEXT")
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")
}

94
sinqEPICSApp/Db/slsvme.db Normal file
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

@ -0,0 +1,31 @@
# Streamdevice protocol file for the PSI SLS VME magnet controller used at AMOR
InTerminator = "\n";
OutTerminator = "\r\n";
#ExtraInput = Ignore;
read {
out "r \$1 \$2";
in "\$1 \$2 %f";
}
readErrTxt {
out "r \$1 errtext";
in "\$1 errtext %s";
}
write {
out "w \$1 \$2 %f";
in "OK";
}
readonoff {
out "r \$1 onoff";
in "\$1 onoff %{off|on}";
}
setpower {
out "w \$1 %{on|off}";
in "OK";
}

View File

@ -0,0 +1,32 @@
# DB definition for a SPS-S5 with the custom RS232 interface as used at SINQ.
#The digital inputs. Ignore the first one, which is the response character R
record(waveform, "$(PREFIX)DigitalInput") {
field(DTYP, "stream")
field(INP, "@spss5.proto readDigital() spss5 0")
field(SCAN, "5 second")
field(NELM, "16")
field(FTVL, "LONG")
field(PREC, "1")
}
# The analog inputs, again ignore the first one as it is the response character A
record(waveform, "$(PREFIX)AnalogInput") {
field(DTYP, "stream")
field(INP, "@spss5.proto readAnalog() spss5 0")
field(SCAN, "5 second")
field(NELM, "8")
field(FTVL, "LONG")
field(PREC, "1")
}
# This is forwarding the S0001 style strings to the hardware. This is the easiest solution but requires
# Hardware knowledge upstream. Which is required anyway. The syntax is: SBBBI with 3 characters for the byte
# to set and one character for the bit in the byte. The Byte must always be filled with 0 to the left.
record(stringout, "$(PREFIX)Push") {
field(DESC, "String for pushing buttons")
field(DTYP, "stream")
field(OUT, "@spss5.proto push() spss5 0")
}

View File

@ -0,0 +1,25 @@
# Streamdevice protocol file for the Siemens SPS-S5 with the custom RS232 interface as used at
# AMOR
InTerminator = "\n";
OutTerminator = "\r\n";
ExtraInput = Ignore;
readDigital {
out "R";
separator="";
in "\?%d";
}
readAnalog {
out "A";
separator="";
in "\?%d";
}
push {
out "%s";
}

View File

@ -5,6 +5,10 @@ USAGE... Motor driver support for the PSI EL734 controller.
Mark Koennecke
February 2013
Updated to have an MsgTxt field through SINQAxis, error handling
Mark Koennecke, May, August 2017
*/
@ -33,18 +37,13 @@ February 2013
* \param[in] numAxes The number of axes that this controller supports
*/
EL734Controller::EL734Controller(const char *portName, const char *EL734PortName, int numAxes)
: asynMotorController(portName, numAxes+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, EL734PortName, numAxes)
{
int axis;
asynStatus status;
EL734Axis *pAxis;
static const char *functionName = "EL734Controller::EL734Controller";
/* Connect to EL734 controller */
status = pasynOctetSyncIO->connect(EL734PortName, 0, &pasynUserController_, NULL);
if (status) {
@ -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);
}
/**
@ -138,18 +136,22 @@ void EL734Controller::switchRemote()
* \param[out] reply The controllers reply
*/
asynStatus EL734Controller::transactController(char command[COMLEN], char reply[COMLEN])
asynStatus EL734Controller::transactController(int axisNo,char command[COMLEN], char reply[COMLEN])
{
asynStatus status;
size_t in, out, i;
int reason;
char myReply[COMLEN];
char myReply[COMLEN], errTxt[256];
SINQAxis *axis = getAxis(axisNo);
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");
}
return status;
}
@ -178,13 +180,25 @@ asynStatus EL734Controller::transactController(char command[COMLEN], char reply[
myReply[i] = (char)tolower((int)reply[i]);
}
if(strstr(myReply,"?cmd") != NULL){
errlogSevPrintf(errlogMajor, "Bad command %s", command);
snprintf(errTxt,sizeof(errTxt), "Bad command %s at axis %d", command, axisNo);
errlogSevPrintf(errlogMajor, errTxt);
if(axis!= NULL){
axis->updateMsgTxtFromDriver(errTxt);
}
return asynError;
} else if(strstr(myReply,"?par") != NULL){
errlogSevPrintf(errlogMajor, "Bad parameter in command %s", command);
snprintf(errTxt,sizeof(errTxt), "Bad parameter in command %s", command);
errlogSevPrintf(errlogMajor, errTxt);
if(axis!= NULL){
axis->updateMsgTxtFromDriver(errTxt);
}
return asynError;
} else if(strstr(myReply,"?rng") != NULL){
errlogSevPrintf(errlogMajor, "Parameter out of range in command %s", command);
snprintf(errTxt,sizeof(errTxt), "Parameter out of range in command %s", command);
errlogSevPrintf(errlogMajor, errTxt);
if(axis!= NULL){
axis->updateMsgTxtFromDriver(errTxt);
}
return asynError;
}
@ -200,9 +214,29 @@ asynStatus EL734Controller::transactController(char command[COMLEN], char reply[
* Initializes register numbers, etc.
*/
EL734Axis::EL734Axis(EL734Controller *pC, int axisNo)
: asynMotorAxis(pC, axisNo),
pC_(pC)
: 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_);
}
}
@ -238,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(command,reply);
status = pC_->transactController(axisNo_,command,reply);
setIntegerParam(pC_->motorStatusProblem_, false);
updateMsgTxtFromDriver("");
next_poll = -1;
return status;
}
@ -252,10 +290,14 @@ 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;
status = pC_->transactController(command,reply);
status = pC_->transactController(axisNo_,command,reply);
return status;
}
@ -270,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_);
@ -277,20 +320,25 @@ asynStatus EL734Axis::moveVelocity(double minVelocity, double maxVelocity, doubl
/* This is a negative move */
sprintf(command, "FB %d", axisNo_);
}
status = pC_->transactController(command,reply);
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(command,reply);
errlogPrintf("Sent STOP on Axis %d\n", axisNo_);
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;
}
@ -301,8 +349,8 @@ asynStatus EL734Axis::setPosition(double position)
//static const char *functionName = "EL734Axis::setPosition";
char command[COMLEN], reply[COMLEN];
sprintf(command, "P %d %f", axisNo_, position/1000.);
status = pC_->transactController(command,reply);
sprintf(command, "U %d %f", axisNo_, position/1000.);
status = pC_->transactController(axisNo_,command,reply);
next_poll = -1;
return status;
@ -328,10 +376,9 @@ asynStatus EL734Axis::setClosedLoop(bool closedLoop)
* \param[out] moving A flag that is set indicating that the axis is moving (true) or done (false). */
asynStatus EL734Axis::poll(bool *moving)
{
int msr;
asynStatus comStatus;
char command[COMLEN], reply[COMLEN];
int msr, count;
asynStatus comStatus = asynSuccess;
char command[COMLEN], reply[COMLEN], errTxt[256];
// protect against excessive polling
if(time(NULL) < next_poll){
@ -340,71 +387,121 @@ asynStatus EL734Axis::poll(bool *moving)
}
// Read the current motor position
setIntegerParam(pC_->motorStatusProblem_,false);
sprintf(command,"u %d", axisNo_);
comStatus = pC_->transactController(command,reply);
if(comStatus) goto skip;
comStatus = pC_->transactController(axisNo_,command,reply);
if(comStatus == asynError){
setIntegerParam(pC_->motorStatusProblem_,true);
goto skip;
}
if(strstr(reply,"*ES") != NULL){
*moving = false;
setIntegerParam(pC_->motorStatusDone_, true);
setIntegerParam(pC_->motorStatusProblem_, true);
errorReported = 1;
updateMsgTxtFromDriver("Emergency Stop Engaged");
comStatus = asynError;
goto skip;
} else if(strstr(reply,"?BSY") != NULL){
*moving = true;
setIntegerParam(pC_->motorStatusDone_, false);
goto skip;
}
sscanf(reply,"%lf", &position);
//errlogPrintf("Axis %d, reply %s, position %lf\n", axisNo_, reply, position);
setDoubleParam(pC_->motorPosition_, position*1000);
//setDoubleParam(pC_->motorEncoderPosition_, position);
count = sscanf(reply,"%lf", &position);
if(count != 1) {
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);
}
// Read the moving status of this motor
sprintf(command,"msr %d",axisNo_);
comStatus = pC_->transactController(command,reply);
if(comStatus) goto skip;
comStatus = pC_->transactController(axisNo_,command,reply);
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");
errorReported = 1;
comStatus = asynError;
goto skip;
} else {
setIntegerParam(pC_->motorStatusLowLimit_, false);
}
if(oredMSR & 0x20){
setIntegerParam(pC_->motorStatusHighLimit_, true);
updateMsgTxtFromDriver("Upper Limit Hit");
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");
errorReported = 1;
comStatus = asynError;
goto skip;
}
if(oredMSR &0x80){
if(oredMSR & 0x100){
setIntegerParam(pC_->motorStatusProblem_, true);
errlogSevPrintf(errlogMajor, "Positioning fault at %d", axisNo_);
errlogSevPrintf(errlogMajor, "Run failure at %d", axisNo_);
updateMsgTxtFromDriver("Run failure");
comStatus = asynError;
errorReported = 1;
goto skip;
}
*moving = false;
setIntegerParam(pC_->motorStatusDone_, true);
} 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);
}
}
skip:
setIntegerParam(pC_->motorStatusProblem_, comStatus ? 1:0);
callParamCallbacks();
return comStatus ? asynError : asynSuccess;
return comStatus;
}
/** Code for iocsh registration */

View File

@ -7,13 +7,13 @@ February 2013
*/
#include "asynMotorController.h"
#include "asynMotorAxis.h"
#include "SINQController.h"
#include "SINQAxis.h"
#define MAX_EL734_AXES 12
#define COMLEN 80
class EL734Axis : public asynMotorAxis
class EL734Axis : public SINQAxis
{
public:
/* These are the methods we override from the base class */
@ -34,11 +34,12 @@ private:
double position;
int homing;
time_t next_poll;
int errorReported;
friend class EL734Controller;
};
class EL734Controller : public asynMotorController {
class EL734Controller : public SINQController {
public:
EL734Controller(const char *portName, const char *EL734PortName, int numAxes);
@ -50,7 +51,7 @@ friend class EL734Axis;
private:
asynUser *pasynUserController_;
asynStatus transactController(char command[COMLEN], char reply[COMLEN]);
asynStatus transactController(int axis, char command[COMLEN], char reply[COMLEN]);
void switchRemote();

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

@ -17,17 +17,18 @@ sinqEPICS_DBD += base.dbd
# Include dbd files from all support applications:
sinqEPICS_DBD += sinq.dbd
sinqEPICS_DBD += pmacAsynIPPort.dbd pmacAsynMotorPort.dbd
#sinqEPICS_DBD += pmacAsynIPPort.dbd pmacAsynMotorPort.dbd
# Add all the support libraries needed by this IOC
sinqEPICS_LIBS += motor asyn std anc350 anc350AsynMotor stream busy
sinqEPICS_LIBS += motor asyn busy synAppsStd streamdevice pcre
# sinqEPICS_registerRecordDeviceDriver.cpp derives from sinqEPICS.dbd
sinqEPICS_SRCS += sinqEPICS_registerRecordDeviceDriver.cpp
sinqEPICS_SRCS += EL734Driver.cpp devScalerEL737.c pmacAsynIPPort.c
sinqEPICS_SRCS += EL734Driver.cpp devScalerEL737.c pmacAsynIPPort.c SINQAxis.cpp SINQController.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

@ -0,0 +1,38 @@
/*
This is a version of the motor axis record which adds code for having an
text field with an error message to go with the motor. The code is
courtesey Torsten Boergershausen from ESS and from the axisRecord.
Mark Koennecke, March 2017
*/
#include "SINQAxis.h"
#include "SINQController.h"
SINQAxis::SINQAxis(class SINQController *pC, int axis)
: asynMotorAxis((asynMotorController *)pC, axis),
pC_(pC)
{
updateMsgTxtFromDriver("");
}
void SINQAxis::updateMsgTxtFromDriver(const char *value)
{
if (value && value[0]) {
pC_->setIntegerParam(axisNo_,pC_->motorMessageIsFromDriver_, 1);
setStringParam(pC_->motorMessageText_,value);
} else {
pC_->setIntegerParam(axisNo_,pC_->motorMessageIsFromDriver_, 0);
setStringParam(pC_->motorMessageText_,"");
}
}
/**
* Sets the value for a string for this axis in the parameter library.
* \param[in] function The function (parameter) number
* \param[in] value Value to set */
asynStatus SINQAxis::setStringParam(int function, const char *value)
{
// Call the base class method
return pC_->setStringParam(axisNo_, function, value);
}

View File

@ -0,0 +1,30 @@
/*
This is a version of the motor axis record which adds code for having an
text field with an error message to go with the motor. The code is
courtesey Torsten Boergershausen from ESS and from the axisRecord.
Mark Koennecke, March 2017
*/
#ifndef __SINQDRIVER
#define __SINQDRIVER
#include "asynMotorAxis.h"
class epicsShareClass SINQAxis : public asynMotorAxis
{
public:
SINQAxis(class SINQController *pC_, int axis);
asynStatus setStringParam(int function, const char *value);
friend class SINQController;
void updateMsgTxtFromDriver(const char *value);
protected:
private:
SINQController *pC_;
};
#endif

View File

@ -0,0 +1,25 @@
/*
This class contains the necessary changes to have an additional text fields for messages
with each axis.
Code lifted from Torsten Boegershausen ESS code.
Mark Koennecke, March 2017
*/
#include "SINQController.h"
#include "asynMotorController.h"
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,
1, // autoconnect
0, 0) // Default priority and stack size
{
createParam(motorMessageIsFromDriverString, asynParamInt32, &motorMessageIsFromDriver_);
createParam(motorMessageTextString, asynParamOctet, &motorMessageText_);
}

View File

@ -0,0 +1,30 @@
/*
This class contains the necessary changes to have an additional text fields for messages
with each axis.
Code lifted from Torsten Boegershausens ESS code.
Mark Koennecke, March 2017
*/
#ifndef __SINQCONTROLLER
#define __SINQCONTROLLER
#include "asynMotorController.h"
#define motorMessageIsFromDriverString "MOTOR_MESSAGE_DRIVER"
#define motorMessageTextString "MOTOR_MESSAGE_TEXT"
class epicsShareClass SINQController : public asynMotorController
{
public:
SINQController(const char *portName, const char *SINQPortName, int numAxes, const int& extraParams=2);
friend class SINQAxis;
protected:
int motorMessageIsFromDriver_;
int motorMessageText_;
};
#endif

View File

@ -19,7 +19,7 @@
* - Thresholding
* - two count modes
* A better solution would be to extend the scalar record to have a proper status and count
* mode field. And threshold control fileds too. But this is much more work, breaks
* mode field. And threshold control fields too. But this is much more work, breaks
* compatability with the scalar record completely and thus was not done for now.
*
* The driver will run a separate thread which does all the
@ -27,6 +27,16 @@
*
* Mark Koennecke, February 2013
*
* 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
*
* Enhanced with a separate thresholdCounter and threshold field in order to replace the
* hack for threshold handling. If these fields are present, the hack with presets 2 and
* 3 as described above is ignored.
*
* Mark Koennecke, August 2017
*/
#include <string.h>
@ -40,6 +50,10 @@
#include <recSup.h>
#include <devSup.h>
#include <cantProceed.h>
#include <dbDefs.h>
#include <dbFldTypes.h>
#include <dbAccess.h>
#include <errlog.h>
#include <scalerRecord.h>
#include <devScaler.h>
@ -49,7 +63,7 @@
/* dset functions */
static long el737_init_record(struct scalerRecord *psr, CALLBACK *pcallback);
static long el737_reset(scalerRecord *psr);
static long el737_read(scalerRecord *psr, unsigned long *val);
static long el737_read(scalerRecord *psr, epicsUInt32 *val);
static long el737_write_preset(scalerRecord *psr, int signal, unsigned long val);
static long el737_arm(scalerRecord *psr, int val);
static long el737_done(scalerRecord *psr);
@ -84,17 +98,87 @@ typedef struct {
unsigned long values[NCOUNT];
unsigned int countCommand;
unsigned int counting;
unsigned long thresholdValue;
unsigned int sendThreshold;
scalerRecord *psr;
epicsEventId wakeUp;
asynUser *asynContext;
CALLBACK *pcallback;
DBADDR pause;
DBADDR status;
DBADDR msgTxt;
DBADDR threshCounter;
DBADDR threshold;
unsigned int dbInit;
}EL737priv;
static void dummyAsynCallback(asynUser *pasynUser)
{
}
static void connectSlaveRecords(EL737priv *priv)
{
char slaveName[PVNAME_SZ], errName[256];
long status;
priv->dbInit = 1;
snprintf(slaveName,sizeof(slaveName),"%s:Pause", priv->psr->name);
errlogPrintf("Name of pause variable: %s\n", slaveName);
status = dbNameToAddr(slaveName,&priv->pause);
if(status!= 0){
errSymLookup(status,errName,sizeof(errName));
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->pause.precord->name);
}
snprintf(slaveName,sizeof(slaveName),"%s:MsgTxt", priv->psr->name);
errlogPrintf("Name of MsgTxt variable: %s\n", slaveName);
status = dbNameToAddr(slaveName,&priv->msgTxt);
if(status!= 0){
errSymLookup(status,errName,sizeof(errName));
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->msgTxt.precord->name);
}
snprintf(slaveName,sizeof(slaveName),"%s:Status", priv->psr->name);
errlogPrintf("Name of status variable: %s\n", slaveName);
status = dbNameToAddr(slaveName,&priv->status);
if(status!= 0){
errSymLookup(status,errName,sizeof(errName));
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);
}
snprintf(slaveName,sizeof(slaveName),"%s:ThresholdCounter", priv->psr->name);
errlogPrintf("Name of thresholdCounter variable: %s\n", slaveName);
status = dbNameToAddr(slaveName,&priv->threshCounter);
if(status!= 0){
errSymLookup(status,errName,sizeof(errName));
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->threshCounter.precord->name);
}
snprintf(slaveName,sizeof(slaveName),"%s:Threshold", priv->psr->name);
errlogPrintf("Name of thresholdCounter variable: %s\n", slaveName);
status = dbNameToAddr(slaveName,&priv->threshold);
if(status!= 0){
errSymLookup(status,errName,sizeof(errName));
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->threshold.precord->name);
}
priv->thresholdValue = -999;
}
static long el737_init_record(scalerRecord *psr, CALLBACK *pcallback)
{
EL737priv *priv = NULL;
@ -122,7 +206,9 @@ static long el737_init_record(scalerRecord *psr, CALLBACK *pcallback)
priv->psr = psr;
priv->wakeUp = epicsEventCreate(epicsEventEmpty);
priv->pcallback = pcallback;
priv->dbInit = 0;
psr->dpvt = priv;
/*
* Hook up with device
@ -139,15 +225,17 @@ 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);
pasynOctetSyncIO->writeRead(priv->asynContext, command, strlen(command),
reply,sizeof(reply), 1.,&out,&in,&reason);
strcpy(command,"ECHO 2");
@ -155,6 +243,7 @@ static long el737_init_record(scalerRecord *psr, CALLBACK *pcallback)
reply,sizeof(reply), 1.,&out,&in,&reason);
pasynManager->freeAsynUser(dummyUser);
connectSlaveRecords(priv);
/*
start the thread which actually runs the device
@ -181,14 +270,14 @@ static long el737_reset(scalerRecord *psr)
return 0;
}
static long el737_read(scalerRecord *psr, unsigned long *val)
static long el737_read(scalerRecord *psr, epicsUInt32 *val)
{
unsigned int i;
EL737priv *priv;
priv = (EL737priv *)psr->dpvt;
for(i = 0; i < NCOUNT; i++){
val[i] = priv->values[i];
val[i] = (epicsUInt32)priv->values[i];
}
return 0;
@ -245,27 +334,63 @@ static long el737_done(scalerRecord *psr)
}
}
static asynStatus el737_transactCommand(asynUser *asynContext, char command[COMLEN],char reply[COMLEN])
static asynStatus el737_transactCommand(EL737priv *priv,char command[COMLEN],char reply[COMLEN])
{
asynStatus status;
size_t in, out;
int reason;
char myCommand[COMLEN];
int reason, dbStatus;
char myCommand[COMLEN], message[40];
pasynOctetSyncIO->flush(asynContext);
pasynOctetSyncIO->flush(priv->asynContext);
status = pasynOctetSyncIO->writeRead(asynContext, command, strlen(command),
strcpy(message,"");
status = pasynOctetSyncIO->writeRead(priv->asynContext, command, strlen(command),
reply,COMLEN, 1.,&out,&in,&reason);
if(status == asynSuccess){
if(strstr(reply,"?OF") != NULL){
strcpy(myCommand,"RMT 1");
status = pasynOctetSyncIO->writeRead(asynContext, myCommand, strlen(myCommand),
status = pasynOctetSyncIO->writeRead(priv->asynContext, myCommand, strlen(myCommand),
reply,COMLEN, 1.,&out,&in,&reason);
strcpy(myCommand,"ECHO 2");
status = pasynOctetSyncIO->writeRead(asynContext, myCommand, strlen(myCommand),
status = pasynOctetSyncIO->writeRead(priv->asynContext, myCommand, strlen(myCommand),
reply,COMLEN, 1.,&out,&in,&reason);
return pasynOctetSyncIO->writeRead(asynContext, command, strlen(command),
return pasynOctetSyncIO->writeRead(priv->asynContext, command, strlen(command),
reply,COMLEN, 1.,&out,&in,&reason);
} else {
if(strstr(reply,"?OV") != NULL){
strncpy(message,"Overflow",sizeof(message));
} else if(strstr(reply,"?1") != NULL) {
strncpy(message,"Par out of range",sizeof(message));
} else if(strstr(reply,"?2") != NULL){
strncpy(message,"Bad command",sizeof(message));
} else if(strstr(reply,"?3") != NULL){
strncpy(message,"Bad parameter",sizeof(message));
} else if(strstr(reply,"?4") != NULL){
strncpy(message,"Bad counter",sizeof(message));
} else if(strstr(reply,"?5") != NULL){
strncpy(message,"Parameter missing",sizeof(message));
} else if(strstr(reply,"?6") != NULL){
strncpy(message,"to many counts",sizeof(message));
} else if(strstr(reply,"?91") != NULL){
strncpy(message,"Start Failure",sizeof(message));
} else if(strstr(reply,"?92") != NULL){
strncpy(message,"Failure while counting",sizeof(message));
} else if(strstr(reply,"?93") != NULL){
strncpy(message,"Read Failure",sizeof(message));
} else {
if(strstr(reply,"?") != NULL) {
snprintf(message,sizeof(message),"HW error: %s", reply);
}
}
}
} else {
strncpy(message,"Lost communication",sizeof(message));
}
if(priv->dbInit){
dbStatus = dbPutField(&priv->msgTxt, DBR_STRING,message, 1);
if(dbStatus!= 0){
errSymLookup(dbStatus,message,sizeof(message));
errlogPrintf("Setting external count message failed with %s\n", message);
}
}
return status;
@ -276,23 +401,66 @@ static asynStatus sendStop(EL737priv *priv)
char command[COMLEN], reply[COMLEN];
//errlogPrintf("Sending stop\n");
strcpy(command,"S");
return el737_transactCommand(priv->asynContext,command,reply);
return el737_transactCommand(priv,command,reply);
}
static void runEvents(EL737priv *priv)
{
char command[COMLEN], reply[COMLEN];
char command[COMLEN], reply[COMLEN], errName[256];
int status;
long dbStatus, myThreshold, nElements = 1, options = 0, threshCounter;
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->asynContext,command,reply);
sprintf(command,"DR %d", (int)priv->presets[THRESHMON]);
status = el737_transactCommand(priv->asynContext,command,reply);
if(status == asynSuccess){
priv->sendThreshold = 0;
/*
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 && priv->sendThreshold) {
dbStatus = dbGetField(&priv->threshold,DBR_LONG,&myThreshold,&options, &nElements,NULL);
if(dbStatus != 0){
errSymLookup(dbStatus,errName,sizeof(errName));
errlogPrintf("Reading threshold failed with %s\n", errName);
} else {
if(myThreshold != priv->thresholdValue){
/*
we have to set the threshold
*/
dbStatus = dbGetField(&priv->threshCounter,DBR_LONG,&threshCounter, &options, &nElements,NULL);
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);
status = el737_transactCommand(priv,command,reply);
sprintf(command,"DR %d", (int)threshCounter);
status = el737_transactCommand(priv, command,reply);
if(status == asynSuccess){
priv->sendThreshold = 0;
priv->thresholdValue = myThreshold;
}
}
}
}
}
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;
}
}
}
@ -307,7 +475,7 @@ static void runEvents(EL737priv *priv)
sprintf(command,"TP %f", priv->presets[TIMER]/1000.);
//errlogPrintf("Starting preset timer\n");
}
status = el737_transactCommand(priv->asynContext,command,reply);
status = el737_transactCommand(priv,command,reply);
if(status == asynSuccess){
priv->counting = 1;
}
@ -335,7 +503,7 @@ static void updateValues(EL737priv *priv)
long m1, m2 ,m3, m4, m5, m6 ,m7, m8;
strcpy(command,"RA");
status = el737_transactCommand(priv->asynContext,command,reply);
status = el737_transactCommand(priv,command,reply);
if(status != asynSuccess){
errlogPrintf("devScalerEL737::el737Thread communication problem %s\n",
priv->asynContext->errorMessage);
@ -371,9 +539,10 @@ static void el737Thread(void *param)
EL737priv *priv = (EL737priv *)param;
epicsEventWaitStatus evStatus;
double timeout = 60.;
char command[COMLEN], reply[COMLEN];
char command[COMLEN], reply[COMLEN], errName[256];
asynStatus status;
int rs;
int rs, ctStatus;
long dbStatus, options, nElements = 1, pauseFlag = 0;
//errlogPrintf("Within EL737 thread \n");
@ -402,7 +571,7 @@ static void el737Thread(void *param)
if(priv->counting) {
strcpy(command,"RS");
status = el737_transactCommand(priv->asynContext,command,reply);
status = el737_transactCommand(priv, command,reply);
if(status != asynSuccess){
errlogPrintf("devScalerEL737::el737Thread communication problem %s\n",
priv->asynContext->errorMessage);
@ -414,15 +583,57 @@ static void el737Thread(void *param)
priv->counting = 0;
timeout = 60.;
priv->values[9] = 0;
ctStatus = 0;
} else if(rs == 1 || rs == 2){
/* counting */
priv->values[9] = 1;
ctStatus = 1;
} else if(rs == 5 || rs == 6){
/* low rate */
priv->values[9] = 2;
ctStatus = 2;
} else if(rs == 9 || rs == 13 || rs == 10 || rs == 14){
/* low rate */
priv->values[9] = 2;
/* paused states */
priv->values[9] = 3;
ctStatus = 3;
}
}
if(priv->dbInit){
/*
update the external status field
*/
dbStatus = dbPutField(&priv->status, DBR_LONG,&ctStatus, 1);
if(dbStatus!= 0){
errSymLookup(dbStatus,errName,sizeof(errName));
errlogPrintf("Setting external count status failed with %s\n", errName);
}
/*
check and handle pause flag
*/
dbStatus = dbGetField(&priv->pause, DBR_LONG,&pauseFlag,&options, &nElements, NULL);
if(dbStatus!= 0){
errSymLookup(dbStatus,errName,sizeof(errName));
errlogPrintf("Reading pauseFlag failed with %s\n", errName);
}
/* errlogPrintf("Successfully read %ld pause flags as %d\n",nElements, pauseFlag); */
if(pauseFlag == 1 && ctStatus == 1){
strcpy(command,"PS");
status = el737_transactCommand(priv, command,reply);
if(status != asynSuccess){
errlogPrintf("devScalerEL737::el737Thread communication problem %s\n",
priv->asynContext->errorMessage);
break;
}
} else if(pauseFlag == 0 && ctStatus == 3){
strcpy(command,"CO");
status = el737_transactCommand(priv,command,reply);
if(status != asynSuccess){
errlogPrintf("devScalerEL737::el737Thread communication problem %s\n",
priv->asynContext->errorMessage);
break;
}
}
}

View File

@ -1,9 +1,19 @@
record(scaler,"")
record(bo,"$(P):Pause")
{
field(NAME,"NZ:counter")
field(DESC,"NARZIS EL737 counter")
field(DTYP,"asynScalerEL737")
field(OUT,"INST_IO @asyn(cter1,0)"
field(DTYP,"Soft Channel")
}
record(longin,"$(P):Status")
{
field(DTYP,"Soft Channel")
}
record(stringin,"$(P):MsgTxt")
{
field(DTYP,"Soft Channel")
}
record(scaler,"$(P)")
{
field(DESC,"$(DESC)")
field(DTYP,"asynScalerEL737")
field(OUT,"INST_IO @asyn($(PORT),0)")
}

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,22 +3,22 @@
#---------------------------------------------
registrar(EL734Register)
registrar(PhytronRegister)
registrar(EuroMoveRegister)
registrar(NanotecRegister)
addpath "/usr/local/epics/support/asyn-4-18/dbd"
addpath "/usr/local/epics/dbd"
addpath "/usr/local/epics/support/motor-6-7/dbd"
addpath "/usr/local/epics/support/std-3-1/dbd"
addpath "/usr/local/epics/anc350v17/dbd"
include "drvAsynIPPort.dbd"
include "motorRecord.dbd"
include "motorSupport.dbd"
include "anc350AsynMotor.dbd"
registrar(pmacControllerRegister)
registrar(pmacAsynIPPortRegister)
#--------------------------------------------------------
# With the PSI module build system, including these items actually
# hurts. It causes missing symbols when loading the shared lib.
#------------------------------------------------------------------
#include "/ioc/modules/asyn/427.0.1/R3.14.12/dbd/asyn.dbd"
#include "/ioc/modules/motorBase/6.9.12/R3.14.12/dbd/motorBase.dbd"
#include "/ioc/modules/SynApps/1.13.0/R3.14.12/dbd/SynApps.dbd"
#include "/ioc/modules/scaler/koennecke/R3.14.12/dbd/scaler.dbd"
include "scalerRecord.dbd"
device(scaler,INST_IO,devScalerEL737,"asynScalerEL737")
#--------- For lakeshore
addpath "/usr/local/epics/support/StreamDevice-2-6/dbd"
include "stream.dbd"
addpath "/usr/local/epics/support/busy-1-4/dbd"
include "busySupport.dbd"
#--------- For lakeshore and magnets
#include "/ioc/modules/stream/2.7.9/R3.14.12/dbd/stream.dbd"
#include "busy.dbd"

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)

7
testcter/startcter.cmd Normal file
View File

@ -0,0 +1,7 @@
require sinq,koennecke
epicsEnvSet("TOP","/afs/psi.ch/project/sinqdev/sinqepicsapp")
drvAsynIPPortConfigure("cter1","localhost:62000",0,0,0)
dbLoadRecords("asynRecord.db","P=KM36:,R=cter1,PORT=cter1,ADDR=0,OMAX=80,IMAX=80")
dbLoadRecords("${TOP}/sinqEPICSApp/Db/el737Record.db","P=KM36:counter,PORT=cter1,DESC=KM36Counter")