Initial commit of the PSI version of motorMotorSim as a standalone
driver.
This commit is contained in:
5
.gitattributes
vendored
Normal file
5
.gitattributes
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
#Which files need CRLF handling
|
||||
* text=auto
|
||||
*.sh eol=lf
|
||||
*.bat eol=crlf
|
||||
*.cmd -text
|
18
.gitignore
vendored
Normal file
18
.gitignore
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
*~
|
||||
O.*
|
||||
*.swp
|
||||
*BAK.adl
|
||||
bin/
|
||||
db/
|
||||
dbd/
|
||||
html/
|
||||
include/
|
||||
lib/
|
||||
templates/
|
||||
cdCommands
|
||||
envPaths
|
||||
dllPath.bat
|
||||
auto_settings.sav*
|
||||
auto_positions.sav*
|
||||
.ccfxprepdir/
|
||||
*.local
|
25
GNUmakefile
Normal file
25
GNUmakefile
Normal file
@ -0,0 +1,25 @@
|
||||
# This build the sinqMotor base class
|
||||
include /ioc/tools/driver.makefile
|
||||
|
||||
MODULE=motorMotorSim
|
||||
BUILDCLASSES=Linux
|
||||
EPICS_VERSIONS=7.0.7
|
||||
ARCH_FILTER=RHEL%
|
||||
|
||||
# Specify the version of asynMotor we want to build against
|
||||
motorBase_VERSION=7.2.2
|
||||
|
||||
# motorRecord.h will be created from motorRecord.dbd
|
||||
# install devMotorSoft.dbd into <top>/dbd
|
||||
DBD += motorSimApp/src/motorSimSupport.dbd
|
||||
|
||||
# Source files to build
|
||||
SOURCES += motorSimApp/src/route.c
|
||||
SOURCES += motorSimApp/src/devMotorSim.c
|
||||
SOURCES += motorSimApp/src/drvMotorSim.c
|
||||
SOURCES += motorSimApp/src/motorSimDriver.cpp
|
||||
SOURCES += motorSimApp/src/motorSimRegister.cc
|
||||
|
||||
USR_CFLAGS += -Wall -Wextra -Weffc++ -Wunused-result # -Werror
|
||||
|
||||
# MISCS would be the place to keep the stream device template files
|
44
Makefile
Normal file
44
Makefile
Normal file
@ -0,0 +1,44 @@
|
||||
# Makefile at top of application tree
|
||||
TOP = .
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
# Directories to build, any order
|
||||
DIRS += configure
|
||||
DIRS += $(wildcard *Sup)
|
||||
DIRS += $(wildcard *App)
|
||||
DIRS += $(wildcard *Top)
|
||||
DIRS += $(wildcard iocBoot)
|
||||
|
||||
ifeq ($(BUILD_IOCS), YES)
|
||||
DIRS += $(wildcard iocs)
|
||||
endif
|
||||
|
||||
# The build order is controlled by these dependency rules:
|
||||
|
||||
# All dirs except configure depend on configure
|
||||
$(foreach dir, $(filter-out configure, $(DIRS)), \
|
||||
$(eval $(dir)_DEPEND_DIRS += configure))
|
||||
|
||||
# Any *App dirs depend on all *Sup dirs
|
||||
$(foreach dir, $(filter %App, $(DIRS)), \
|
||||
$(eval $(dir)_DEPEND_DIRS += $(filter %Sup, $(DIRS))))
|
||||
|
||||
# Any *Top dirs depend on all *Sup and *App dirs
|
||||
$(foreach dir, $(filter %Top, $(DIRS)), \
|
||||
$(eval $(dir)_DEPEND_DIRS += $(filter %Sup %App, $(DIRS))))
|
||||
|
||||
# iocBoot depends on all *App dirs
|
||||
iocBoot_DEPEND_DIRS += $(filter %App,$(DIRS))
|
||||
|
||||
# Add any additional dependency rules here:
|
||||
|
||||
# iocs depend on all *Sup and *App dirs
|
||||
$(foreach dir, $(filter %iocs, $(DIRS)), \
|
||||
$(eval $(dir)_DEPEND_DIRS += $(filter %Sup %App, $(DIRS))))
|
||||
|
||||
# Only support top-level targets when submodule is built stand-alone
|
||||
ifeq ($(INSTALL_LOCATION),$(MOTOR))
|
||||
include $(TOP)/configure/RULES_DIRS
|
||||
else
|
||||
include $(TOP)/configure/RULES_TOP
|
||||
endif
|
7
README.md
Normal file
7
README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# PSI mirror of Marche
|
||||
|
||||
This repository is a PSI-internal mirror of commit 6e2f646 of the motorMotorSim repository of epics-motor (https://github.com/epics-motor/motorMotorSim/tree/6e2f64601e5f27ba212fc8e8488ac655cc4bb483). A GNUmakefile has been added which builds the simulated motor driver as a module of the GFA build system.
|
||||
|
||||
## How to build it
|
||||
|
||||
Clone this repository to a suitable location (`git clone https://git.psi.ch/sinq-epics-modules/motormotorsim#`). Afterwards, switch to the directory (`cd motormotorsim`) and run `make install`.
|
29
configure/CONFIG
Normal file
29
configure/CONFIG
Normal file
@ -0,0 +1,29 @@
|
||||
# CONFIG - Load build configuration data
|
||||
#
|
||||
# Do not make changes to this file!
|
||||
|
||||
# Allow user to override where the build rules come from
|
||||
RULES = $(EPICS_BASE)
|
||||
|
||||
# RELEASE files point to other application tops
|
||||
include $(TOP)/configure/RELEASE
|
||||
-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).Common
|
||||
ifdef T_A
|
||||
-include $(TOP)/configure/RELEASE.Common.$(T_A)
|
||||
-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A)
|
||||
endif
|
||||
|
||||
CONFIG = $(RULES)/configure
|
||||
include $(CONFIG)/CONFIG
|
||||
|
||||
# Override the Base definition:
|
||||
INSTALL_LOCATION = $(TOP)
|
||||
|
||||
# CONFIG_SITE files contain other build configuration settings
|
||||
include $(TOP)/configure/CONFIG_SITE
|
||||
-include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).Common
|
||||
ifdef T_A
|
||||
-include $(TOP)/configure/CONFIG_SITE.Common.$(T_A)
|
||||
-include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A)
|
||||
endif
|
||||
|
43
configure/CONFIG_SITE
Normal file
43
configure/CONFIG_SITE
Normal file
@ -0,0 +1,43 @@
|
||||
# CONFIG_SITE
|
||||
|
||||
# Make any application-specific changes to the EPICS build
|
||||
# configuration variables in this file.
|
||||
#
|
||||
# Host/target specific settings can be specified in files named
|
||||
# CONFIG_SITE.$(EPICS_HOST_ARCH).Common
|
||||
# CONFIG_SITE.Common.$(T_A)
|
||||
# CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A)
|
||||
|
||||
# CHECK_RELEASE controls the consistency checking of the support
|
||||
# applications pointed to by the RELEASE* files.
|
||||
# Normally CHECK_RELEASE should be set to YES.
|
||||
# Set CHECK_RELEASE to NO to disable checking completely.
|
||||
# Set CHECK_RELEASE to WARN to perform consistency checking but
|
||||
# continue building even if conflicts are found.
|
||||
CHECK_RELEASE = YES
|
||||
|
||||
# Set this when you only want to compile this application
|
||||
# for a subset of the cross-compiled target architectures
|
||||
# that Base is built for.
|
||||
#CROSS_COMPILER_TARGET_ARCHS = vxWorks-ppc32
|
||||
|
||||
# To install files into a location other than $(TOP) define
|
||||
# INSTALL_LOCATION here.
|
||||
#INSTALL_LOCATION=</absolute/path/to/install/top>
|
||||
|
||||
# Set this when the IOC and build host use different paths
|
||||
# to the install location. This may be needed to boot from
|
||||
# a Microsoft FTP server say, or on some NFS configurations.
|
||||
#IOCS_APPL_TOP = </IOC's/absolute/path/to/install/top>
|
||||
|
||||
# For application debugging purposes, override the HOST_OPT and/
|
||||
# or CROSS_OPT settings from base/configure/CONFIG_SITE
|
||||
#HOST_OPT = NO
|
||||
#CROSS_OPT = NO
|
||||
|
||||
# These allow developers to override the CONFIG_SITE variable
|
||||
# settings without having to modify the configure/CONFIG_SITE
|
||||
# file itself.
|
||||
-include $(TOP)/../CONFIG_SITE.local
|
||||
-include $(TOP)/configure/CONFIG_SITE.local
|
||||
|
12
configure/Makefile
Normal file
12
configure/Makefile
Normal file
@ -0,0 +1,12 @@
|
||||
TOP=..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
ifeq ($(INSTALL_CONFIG),$(MOTOR)/configure)
|
||||
# Don't overwrite Motor's configure files
|
||||
INSTALL_CONFIG =
|
||||
endif
|
||||
|
||||
TARGETS = $(CONFIG_TARGETS)
|
||||
|
||||
include $(TOP)/configure/RULES
|
7
configure/RELEASE
Normal file
7
configure/RELEASE
Normal file
@ -0,0 +1,7 @@
|
||||
# RELEASE - Location of external support modules
|
||||
|
||||
# Use motor/module's generated release file when buidling inside motor
|
||||
-include $(TOP)/../RELEASE.$(EPICS_HOST_ARCH).local
|
||||
# Use motorMotorSim's RELEASE.local when building outside motor
|
||||
-include $(TOP)/configure/RELEASE.local
|
||||
|
6
configure/RULES
Normal file
6
configure/RULES
Normal file
@ -0,0 +1,6 @@
|
||||
# RULES
|
||||
|
||||
include $(CONFIG)/RULES
|
||||
|
||||
# Library should be rebuilt because LIBOBJS may have changed.
|
||||
$(LIBNAME): ../Makefile
|
2
configure/RULES.ioc
Normal file
2
configure/RULES.ioc
Normal file
@ -0,0 +1,2 @@
|
||||
#RULES.ioc
|
||||
include $(CONFIG)/RULES.ioc
|
2
configure/RULES_DIRS
Normal file
2
configure/RULES_DIRS
Normal file
@ -0,0 +1,2 @@
|
||||
#RULES_DIRS
|
||||
include $(CONFIG)/RULES_DIRS
|
3
configure/RULES_TOP
Normal file
3
configure/RULES_TOP
Normal file
@ -0,0 +1,3 @@
|
||||
#RULES_TOP
|
||||
include $(CONFIG)/RULES_TOP
|
||||
|
33
docs/RELEASE.md
Normal file
33
docs/RELEASE.md
Normal file
@ -0,0 +1,33 @@
|
||||
# motorMotorSim Releases
|
||||
|
||||
## __R1-1 (2020-05-12)__
|
||||
R1-1 is a release based on the master branch.
|
||||
|
||||
### Changes since R1-0
|
||||
|
||||
#### New features
|
||||
* None
|
||||
|
||||
#### Modifications to existing features
|
||||
* Commit [d61a030](https://github.com/epics-motor/motorMotorSim/commit/d61a03052df7fd066d30d88c733022ea3ce1e12b): iocsh files are now installed to the top-level directory
|
||||
|
||||
#### Bug fixes
|
||||
* Pull request [#2](https://github.com/epics-motor/motorMotorSim/pull/2): Fix setting home bit after home operation
|
||||
* Commit [ed21eac](https://github.com/epics-motor/motorMotorSim/commit/ed21eac09615ec5bb8bde44a13df28cd651b0f35): Include ``$(MOTOR)/modules/RELEASE.$(EPICS_HOST_ARCH).local`` instead of ``$(MOTOR)/configure/RELEASE``
|
||||
|
||||
## __R1-0 (2019-04-18)__
|
||||
R1-0 is a release based on the master branch.
|
||||
|
||||
### Changes since motor-6-11
|
||||
|
||||
motorMotorSim is now a standalone module, as well as a submodule of [motor](https://github.com/epics-modules/motor)
|
||||
|
||||
#### New features
|
||||
* motorMotorSim can be built outside of the motor directory
|
||||
* motorMotorSim has a dedicated example IOC that can be built outside of motorMotorSim
|
||||
|
||||
#### Modifications to existing features
|
||||
* None
|
||||
|
||||
#### Bug fixes
|
||||
* None
|
25
iocs/Makefile
Normal file
25
iocs/Makefile
Normal file
@ -0,0 +1,25 @@
|
||||
TOP = ..
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
DIRS += motorSimIOC
|
||||
|
||||
include $(TOP)/configure/RULES_TOP
|
||||
|
||||
uninstallTargets = $(foreach dir, $(DIRS), $(dir)$(DIVIDER)uninstall)
|
||||
uninstall: $(uninstallTargets)
|
||||
define UNINSTALL_template
|
||||
$(1)$(DIVIDER)uninstall:
|
||||
$(MAKE) -C $(1) uninstall
|
||||
endef
|
||||
$(foreach dir, $(DIRS), $(eval $(call UNINSTALL_template,$(dir))))
|
||||
.PHONY: uninstall $(uninstallTargets)
|
||||
|
||||
realuninstallTargets = $(foreach dir, $(DIRS), $(dir)$(DIVIDER)realuninstall)
|
||||
realuninstall: $(realuninstallTargets)
|
||||
define REALUNINSTALL_template
|
||||
$(1)$(DIVIDER)realuninstall:
|
||||
$(MAKE) -C $(1) realuninstall
|
||||
endef
|
||||
$(foreach dir, $(DIRS), $(eval $(call REALUNINSTALL_template,$(dir))))
|
||||
.PHONY: realuninstall $(realuninstallTargets)
|
||||
|
31
iocs/motorSimIOC/Makefile
Normal file
31
iocs/motorSimIOC/Makefile
Normal file
@ -0,0 +1,31 @@
|
||||
# Makefile at top of application tree
|
||||
TOP = .
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
# Directories to build, any order
|
||||
DIRS += configure
|
||||
DIRS += $(wildcard *Sup)
|
||||
DIRS += $(wildcard *App)
|
||||
DIRS += $(wildcard *Top)
|
||||
DIRS += $(wildcard iocBoot)
|
||||
|
||||
# The build order is controlled by these dependency rules:
|
||||
|
||||
# All dirs except configure depend on configure
|
||||
$(foreach dir, $(filter-out configure, $(DIRS)), \
|
||||
$(eval $(dir)_DEPEND_DIRS += configure))
|
||||
|
||||
# Any *App dirs depend on all *Sup dirs
|
||||
$(foreach dir, $(filter %App, $(DIRS)), \
|
||||
$(eval $(dir)_DEPEND_DIRS += $(filter %Sup, $(DIRS))))
|
||||
|
||||
# Any *Top dirs depend on all *Sup and *App dirs
|
||||
$(foreach dir, $(filter %Top, $(DIRS)), \
|
||||
$(eval $(dir)_DEPEND_DIRS += $(filter %Sup %App, $(DIRS))))
|
||||
|
||||
# iocBoot depends on all *App dirs
|
||||
iocBoot_DEPEND_DIRS += $(filter %App,$(DIRS))
|
||||
|
||||
# Add any additional dependency rules here:
|
||||
|
||||
include $(TOP)/configure/RULES_TOP
|
29
iocs/motorSimIOC/configure/CONFIG
Normal file
29
iocs/motorSimIOC/configure/CONFIG
Normal file
@ -0,0 +1,29 @@
|
||||
# CONFIG - Load build configuration data
|
||||
#
|
||||
# Do not make changes to this file!
|
||||
|
||||
# Allow user to override where the build rules come from
|
||||
RULES = $(EPICS_BASE)
|
||||
|
||||
# RELEASE files point to other application tops
|
||||
include $(TOP)/configure/RELEASE
|
||||
-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).Common
|
||||
ifdef T_A
|
||||
-include $(TOP)/configure/RELEASE.Common.$(T_A)
|
||||
-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A)
|
||||
endif
|
||||
|
||||
CONFIG = $(RULES)/configure
|
||||
include $(CONFIG)/CONFIG
|
||||
|
||||
# Override the Base definition:
|
||||
INSTALL_LOCATION = $(TOP)
|
||||
|
||||
# CONFIG_SITE files contain other build configuration settings
|
||||
include $(TOP)/configure/CONFIG_SITE
|
||||
-include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).Common
|
||||
ifdef T_A
|
||||
-include $(TOP)/configure/CONFIG_SITE.Common.$(T_A)
|
||||
-include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A)
|
||||
endif
|
||||
|
44
iocs/motorSimIOC/configure/CONFIG_SITE
Normal file
44
iocs/motorSimIOC/configure/CONFIG_SITE
Normal file
@ -0,0 +1,44 @@
|
||||
# CONFIG_SITE
|
||||
|
||||
# Make any application-specific changes to the EPICS build
|
||||
# configuration variables in this file.
|
||||
#
|
||||
# Host/target specific settings can be specified in files named
|
||||
# CONFIG_SITE.$(EPICS_HOST_ARCH).Common
|
||||
# CONFIG_SITE.Common.$(T_A)
|
||||
# CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A)
|
||||
|
||||
# CHECK_RELEASE controls the consistency checking of the support
|
||||
# applications pointed to by the RELEASE* files.
|
||||
# Normally CHECK_RELEASE should be set to YES.
|
||||
# Set CHECK_RELEASE to NO to disable checking completely.
|
||||
# Set CHECK_RELEASE to WARN to perform consistency checking but
|
||||
# continue building even if conflicts are found.
|
||||
CHECK_RELEASE = YES
|
||||
|
||||
# Set this when you only want to compile this application
|
||||
# for a subset of the cross-compiled target architectures
|
||||
# that Base is built for.
|
||||
#CROSS_COMPILER_TARGET_ARCHS = vxWorks-ppc32
|
||||
|
||||
# To install files into a location other than $(TOP) define
|
||||
# INSTALL_LOCATION here.
|
||||
#INSTALL_LOCATION=</absolute/path/to/install/top>
|
||||
|
||||
# Set this when the IOC and build host use different paths
|
||||
# to the install location. This may be needed to boot from
|
||||
# a Microsoft FTP server say, or on some NFS configurations.
|
||||
#IOCS_APPL_TOP = </IOC's/absolute/path/to/install/top>
|
||||
|
||||
# For application debugging purposes, override the HOST_OPT and/
|
||||
# or CROSS_OPT settings from base/configure/CONFIG_SITE
|
||||
#HOST_OPT = NO
|
||||
#CROSS_OPT = NO
|
||||
|
||||
# Include motor's CONFIG_SITE.local when building inside motor
|
||||
-include $(TOP)/../../../../configure/CONFIG_SITE.local
|
||||
# Include motorMotorSim's CONFIG_SITE.local when building inside motorMotorSim
|
||||
-include $(TOP)/../../configure/CONFIG_SITE.local
|
||||
# Use motorSimIOC's CONFIG_SITE.local
|
||||
-include $(TOP)/configure/CONFIG_SITE.local
|
||||
|
8
iocs/motorSimIOC/configure/Makefile
Normal file
8
iocs/motorSimIOC/configure/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
TOP=..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
TARGETS = $(CONFIG_TARGETS)
|
||||
CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS)))
|
||||
|
||||
include $(TOP)/configure/RULES
|
8
iocs/motorSimIOC/configure/RELEASE
Normal file
8
iocs/motorSimIOC/configure/RELEASE
Normal file
@ -0,0 +1,8 @@
|
||||
# RELEASE - Location of external support modules
|
||||
|
||||
# Use motor/module's generated release file when buidling inside motor
|
||||
-include $(TOP)/../../../RELEASE.$(EPICS_HOST_ARCH).local
|
||||
# Use motorMotorSim's release file when building inside motorMotorSim, but outside motor
|
||||
-include $(TOP)/../../configure/RELEASE.local
|
||||
# Use motorSimIOC's RELEASE.local when building outside motorMotorSim
|
||||
-include $(TOP)/configure/RELEASE.local
|
6
iocs/motorSimIOC/configure/RULES
Normal file
6
iocs/motorSimIOC/configure/RULES
Normal file
@ -0,0 +1,6 @@
|
||||
# RULES
|
||||
|
||||
include $(CONFIG)/RULES
|
||||
|
||||
# Library should be rebuilt because LIBOBJS may have changed.
|
||||
$(LIBNAME): ../Makefile
|
2
iocs/motorSimIOC/configure/RULES.ioc
Normal file
2
iocs/motorSimIOC/configure/RULES.ioc
Normal file
@ -0,0 +1,2 @@
|
||||
#RULES.ioc
|
||||
include $(CONFIG)/RULES.ioc
|
2
iocs/motorSimIOC/configure/RULES_DIRS
Normal file
2
iocs/motorSimIOC/configure/RULES_DIRS
Normal file
@ -0,0 +1,2 @@
|
||||
#RULES_DIRS
|
||||
include $(CONFIG)/RULES_DIRS
|
3
iocs/motorSimIOC/configure/RULES_TOP
Normal file
3
iocs/motorSimIOC/configure/RULES_TOP
Normal file
@ -0,0 +1,3 @@
|
||||
#RULES_TOP
|
||||
include $(CONFIG)/RULES_TOP
|
||||
|
6
iocs/motorSimIOC/iocBoot/Makefile
Normal file
6
iocs/motorSimIOC/iocBoot/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
TOP = ..
|
||||
include $(TOP)/configure/CONFIG
|
||||
DIRS += $(wildcard *ioc*)
|
||||
DIRS += $(wildcard as*)
|
||||
include $(CONFIG)/RULES_DIRS
|
||||
|
9
iocs/motorSimIOC/iocBoot/iocMotorSim/Makefile
Executable file
9
iocs/motorSimIOC/iocBoot/iocMotorSim/Makefile
Executable file
@ -0,0 +1,9 @@
|
||||
TOP = ../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
#ARCH = linux-x86_64
|
||||
#ARCH = vxWorks-ppc32
|
||||
ARCH = $(EPICS_HOST_ARCH)
|
||||
TARGETS = cdCommands
|
||||
TARGETS += envPaths
|
||||
include $(TOP)/configure/RULES.ioc
|
||||
|
24
iocs/motorSimIOC/iocBoot/iocMotorSim/README
Normal file
24
iocs/motorSimIOC/iocBoot/iocMotorSim/README
Normal file
@ -0,0 +1,24 @@
|
||||
To build any examples;
|
||||
|
||||
- in <motor>/configure/RELEASE: EPICS_BASE and MOTOR must be defined.
|
||||
|
||||
- in <motor>/Makefile: the following three lines must be uncommented;
|
||||
#!DIRS += motorExApp iocBoot
|
||||
#!motorExApp_DEPEND_DIRS = motorApp
|
||||
#!iocBoot_DEPEND_DIRS = motorExApp
|
||||
|
||||
To build this simulation example;
|
||||
|
||||
- in <motor>/configure/RELEASE: ASYN must be defined.
|
||||
|
||||
- in <motor>/iocBoot/iocSim/Makefile set the target architecture
|
||||
|
||||
Finally, cd <motor>; gnumake clean uninstall; gnumake
|
||||
|
||||
To run this simulation example on a Unix OS;
|
||||
- Set the EPICS_HOST_ARCH environment variable correctly.
|
||||
- Edit the st.cmd.unix file for either sun or linux.
|
||||
- Start the ioc from this directory by executing the following command.
|
||||
|
||||
../../bin/${EPICS_HOST_ARCH}/WithAsyn st.cmd.unix
|
||||
|
14
iocs/motorSimIOC/iocBoot/iocMotorSim/motor.substitutions
Normal file
14
iocs/motorSimIOC/iocBoot/iocMotorSim/motor.substitutions
Normal file
@ -0,0 +1,14 @@
|
||||
file "$(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}
|
||||
{IOC:, 1, "m$(N)", "asynMotor", motorSim1, 0, "motor $(N)", degrees, Pos, 1, .1, .2, 0, 1, .2, 0.01, 5, 100, -100, ""}
|
||||
{IOC:, 2, "m$(N)", "asynMotor", motorSim1, 1, "motor $(N)", degrees, Pos, 1, .1, .2, 0, 1, .2, 0.01, 5, 100, -100, ""}
|
||||
{IOC:, 3, "m$(N)", "asynMotor", motorSim1, 2, "motor $(N)", degrees, Pos, 1, .1, .2, 0, 1, .2, 0.01, 5, 100, -100, ""}
|
||||
{IOC:, 4, "m$(N)", "asynMotor", motorSim1, 3, "motor $(N)", degrees, Pos, 1, .1, .2, 0, 1, .2, 0.01, 5, 100, -100, ""}
|
||||
|
||||
{IOC:, 5, "m$(N)", "asynMotor", motorSim2, 0, "motor $(N)", degrees, Pos, 1, .1, .2, 0, 1, .2, 0.01, 5, 100, -100, ""}
|
||||
{IOC:, 6, "m$(N)", "asynMotor", motorSim2, 1, "motor $(N)", degrees, Pos, 1, .1, .2, 0, 1, .2, 0.01, 5, 100, -100, ""}
|
||||
{IOC:, 7, "m$(N)", "asynMotor", motorSim2, 2, "motor $(N)", degrees, Pos, 1, .1, .2, 0, 1, .2, 0.01, 5, 100, -100, ""}
|
||||
{IOC:, 8, "m$(N)", "asynMotor", motorSim2, 3, "motor $(N)", degrees, Pos, 1, .1, .2, 0, 1, .2, 0.01, 5, 100, -100, ""}
|
||||
}
|
24
iocs/motorSimIOC/iocBoot/iocMotorSim/st.cmd
Normal file
24
iocs/motorSimIOC/iocBoot/iocMotorSim/st.cmd
Normal file
@ -0,0 +1,24 @@
|
||||
#!../../bin/linux-x86_64/motorSim
|
||||
|
||||
< envPaths
|
||||
|
||||
cd "${TOP}"
|
||||
|
||||
## Register all support components
|
||||
dbLoadDatabase "dbd/motorSim.dbd"
|
||||
motorSim_registerRecordDeviceDriver pdbbase
|
||||
|
||||
cd "${TOP}/iocBoot/${IOC}"
|
||||
|
||||
|
||||
## motorUtil (allstop & alldone)
|
||||
dbLoadRecords("$(MOTOR)/db/motorUtil.db", "P=motorSim:")
|
||||
|
||||
##
|
||||
|
||||
iocInit
|
||||
|
||||
## motorUtil (allstop & alldone)
|
||||
motorUtilInit("motorSim:")
|
||||
|
||||
# Boot complete
|
29
iocs/motorSimIOC/iocBoot/iocMotorSim/st.cmd.Vx
Normal file
29
iocs/motorSimIOC/iocBoot/iocMotorSim/st.cmd.Vx
Normal file
@ -0,0 +1,29 @@
|
||||
# The is the ASYN example for communication to 4 simulated motors
|
||||
# "#!" marks lines that can be uncommented.
|
||||
|
||||
# The following must be added for many board support packages
|
||||
#!cd "... IOC st.cmd complete directory path ... "
|
||||
|
||||
< cdCommands
|
||||
#!< ../nfsCommands
|
||||
|
||||
cd topbin
|
||||
|
||||
# If the VxWorks kernel was built using the project facility, the following must
|
||||
# be added before any C++ code is loaded (see SPR #28980).
|
||||
sysCplusEnable=1
|
||||
|
||||
ld(0,0,"motorSim.munch")
|
||||
|
||||
cd startup
|
||||
dbLoadDatabase("$(TOP)/dbd/motorSim.dbd")
|
||||
WithAsynVx_registerRecordDeviceDriver(pdbbase)
|
||||
|
||||
dbLoadTemplate("motor.substitutions")
|
||||
|
||||
# Create simulated motors: ( start card , start axis , low limit, high limit, home posn, # cards, # axes to setup)
|
||||
motorSimCreate( 0, 0, -32000, 32000, 0, 1, 4 )
|
||||
# Setup the Asyn layer (portname, low-level driver drvet name, card, number of axes on card)
|
||||
drvAsynMotorConfigure("motorSim1", "motorSim", 0, 4)
|
||||
|
||||
iocInit
|
26
iocs/motorSimIOC/iocBoot/iocMotorSim/st.cmd.unix
Normal file
26
iocs/motorSimIOC/iocBoot/iocMotorSim/st.cmd.unix
Normal file
@ -0,0 +1,26 @@
|
||||
# The is the ASYN example for communication to 4 simulated motors
|
||||
# "#!" marks lines that can be uncommented.
|
||||
|
||||
< envPaths
|
||||
|
||||
dbLoadDatabase("$(TOP)/dbd/motorSim.dbd")
|
||||
motorSim_registerRecordDeviceDriver(pdbbase)
|
||||
|
||||
dbLoadTemplate("motor.substitutions")
|
||||
|
||||
# Create simulated motors: ( start card , start axis , low limit, high limit, home posn, # cards, # axes to setup)
|
||||
motorSimCreate( 0, 0, -32000, 32000, 0, 1, 4 )
|
||||
# Setup the Asyn layer (portname, low-level driver drvet name, card, number of axes on card)
|
||||
drvAsynMotorConfigure("motorSim1", "motorSim", 0, 4)
|
||||
|
||||
motorSimCreateController("motorSim2", 8)
|
||||
#asynSetTraceIOMask("motorSim2", 0, 4)
|
||||
#asynSetTraceMask("motorSim2", 0, 255)
|
||||
|
||||
# motorSimConfigAxis(port, axis, lowLimit, highLimit, home, start)
|
||||
motorSimConfigAxis("motorSim2", 0, 20000, -20000, 500, 0)
|
||||
motorSimConfigAxis("motorSim2", 1, 20000, -20000, 1500, 0)
|
||||
motorSimConfigAxis("motorSim2", 2, 20000, -20000, 2500, 0)
|
||||
motorSimConfigAxis("motorSim2", 3, 20000, -20000, 3000, 0)
|
||||
|
||||
iocInit
|
16
iocs/motorSimIOC/iocBoot/iocMotorSim/st.cmd.win32
Executable file
16
iocs/motorSimIOC/iocBoot/iocMotorSim/st.cmd.win32
Executable file
@ -0,0 +1,16 @@
|
||||
# The is the ASYN example for communication to 4 simulated motors
|
||||
# "#!" marks lines that can be uncommented.
|
||||
|
||||
< envPaths
|
||||
|
||||
dbLoadDatabase("$(TOP)/dbd/motorSim.dbd")
|
||||
motorSim_registerRecordDeviceDriver(pdbbase)
|
||||
|
||||
dbLoadTemplate("motor.substitutions")
|
||||
|
||||
# Create simulated motors: ( start card , start axis , low limit, high limit, home posn, # cards, # axes to setup)
|
||||
motorSimCreate( 0, 0, -32000, 32000, 0, 1, 4 )
|
||||
# Setup the Asyn layer (portname, low-level driver drvet name, card, number of axes on card)
|
||||
drvAsynMotorConfigure("motorSim1", "motorSim", 0, 4)
|
||||
|
||||
iocInit
|
1
iocs/motorSimIOC/iocBoot/iocMotorSim/start_medm
Executable file
1
iocs/motorSimIOC/iocBoot/iocMotorSim/start_medm
Executable file
@ -0,0 +1 @@
|
||||
medm -x -macro "P=IOC:, M1=m1, M2=m2, M3=m3, M4=m4, M5=m5, M6=m6, M7=m7, M8=m8" motor8x.adl &
|
22
iocs/motorSimIOC/motorSimApp/Db/Makefile
Normal file
22
iocs/motorSimIOC/motorSimApp/Db/Makefile
Normal file
@ -0,0 +1,22 @@
|
||||
TOP=../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
#----------------------------------------
|
||||
# ADD MACRO DEFINITIONS AFTER THIS LINE
|
||||
|
||||
#----------------------------------------------------
|
||||
# Optimization of db files using dbst (DEFAULT: NO)
|
||||
#DB_OPT = YES
|
||||
|
||||
#----------------------------------------------------
|
||||
# Create and install (or just install) into <top>/db
|
||||
# databases, templates, substitutions like this
|
||||
#DB += xxx.db
|
||||
|
||||
#----------------------------------------------------
|
||||
# If <anyname>.db template is not named <anyname>*.template add
|
||||
# <anyname>_template = <templatename>
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
#----------------------------------------
|
||||
# ADD RULES AFTER THIS LINE
|
||||
|
8
iocs/motorSimIOC/motorSimApp/Makefile
Normal file
8
iocs/motorSimIOC/motorSimApp/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
TOP = ..
|
||||
include $(TOP)/configure/CONFIG
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *src*))
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Src*))
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *db*))
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Db*))
|
||||
include $(TOP)/configure/RULES_DIRS
|
||||
|
56
iocs/motorSimIOC/motorSimApp/src/Makefile
Normal file
56
iocs/motorSimIOC/motorSimApp/src/Makefile
Normal file
@ -0,0 +1,56 @@
|
||||
TOP=../..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
#----------------------------------------
|
||||
# ADD MACRO DEFINITIONS AFTER THIS LINE
|
||||
#=============================
|
||||
|
||||
# The following are used for debugging messages.
|
||||
#!USR_CXXFLAGS += -DDEBUG
|
||||
|
||||
#=============================
|
||||
# Build the IOC application
|
||||
|
||||
PROD_IOC = motorSim
|
||||
# motorSim.dbd will be created and installed
|
||||
DBD += motorSim.dbd
|
||||
|
||||
# motorSim.dbd will be made up from these files:
|
||||
motorSim_DBD += base.dbd
|
||||
|
||||
# Include dbd files from all support applications:
|
||||
#ifdef ASYN
|
||||
motorSim_DBD += asyn.dbd
|
||||
#endif
|
||||
motorSim_DBD += motorSupport.dbd
|
||||
motorSim_DBD += motorSimSupport.dbd
|
||||
|
||||
# Add all the support libraries needed by this IOC
|
||||
motorSim_LIBS += motorSimSupport
|
||||
motorSim_LIBS += motor
|
||||
#ifdef ASYN
|
||||
motorSim_LIBS += asyn
|
||||
#endif
|
||||
#ifdef SNCSEQ
|
||||
motorSim_LIBS += seq pv
|
||||
#endif
|
||||
|
||||
# motorSim_registerRecordDeviceDriver.cpp derives from motorSim.dbd
|
||||
motorSim_SRCS += motorSim_registerRecordDeviceDriver.cpp
|
||||
|
||||
# Build the main IOC entry point on workstation OSs.
|
||||
motorSim_SRCS_DEFAULT += motorSimMain.cpp
|
||||
motorSim_SRCS_vxWorks += -nil-
|
||||
|
||||
# Add support from base/src/vxWorks if needed
|
||||
#motorSim_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary
|
||||
|
||||
# Finally link to the EPICS Base libraries
|
||||
motorSim_LIBS += $(EPICS_BASE_IOC_LIBS)
|
||||
|
||||
#===========================
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
#----------------------------------------
|
||||
# ADD RULES AFTER THIS LINE
|
||||
|
23
iocs/motorSimIOC/motorSimApp/src/motorSimMain.cpp
Normal file
23
iocs/motorSimIOC/motorSimApp/src/motorSimMain.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
/* motorSimMain.cpp */
|
||||
/* Author: Marty Kraimer Date: 17MAR2000 */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "epicsExit.h"
|
||||
#include "epicsThread.h"
|
||||
#include "iocsh.h"
|
||||
|
||||
int main(int argc,char *argv[])
|
||||
{
|
||||
if(argc>=2) {
|
||||
iocsh(argv[1]);
|
||||
epicsThreadSleep(.2);
|
||||
}
|
||||
iocsh(NULL);
|
||||
epicsExit(0);
|
||||
return(0);
|
||||
}
|
22
motorSimApp/Db/Makefile
Normal file
22
motorSimApp/Db/Makefile
Normal file
@ -0,0 +1,22 @@
|
||||
TOP=../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
#----------------------------------------
|
||||
# ADD MACRO DEFINITIONS AFTER THIS LINE
|
||||
|
||||
#----------------------------------------------------
|
||||
# Optimization of db files using dbst (DEFAULT: NO)
|
||||
#DB_OPT = YES
|
||||
|
||||
#----------------------------------------------------
|
||||
# Create and install (or just install) into <top>/db
|
||||
# databases, templates, substitutions like this
|
||||
#DB += xxx.db
|
||||
|
||||
#----------------------------------------------------
|
||||
# If <anyname>.db template is not named <anyname>*.template add
|
||||
# <anyname>_template = <templatename>
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
#----------------------------------------
|
||||
# ADD RULES AFTER THIS LINE
|
||||
|
8
motorSimApp/Makefile
Normal file
8
motorSimApp/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
TOP = ..
|
||||
include $(TOP)/configure/CONFIG
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *src*))
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Src*))
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *db*))
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Db*))
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *iocsh*))
|
||||
include $(TOP)/configure/RULES_DIRS
|
7
motorSimApp/iocsh/EXAMPLE_motorSim.substitutions
Normal file
7
motorSimApp/iocsh/EXAMPLE_motorSim.substitutions
Normal file
@ -0,0 +1,7 @@
|
||||
file "$(MOTOR)/db/asyn_motor.db"
|
||||
{
|
||||
pattern
|
||||
{N, M, ADDR, DESC, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, MRES, PREC, INIT}
|
||||
{1, "m$(N)", 0, "motor $(N)", degrees, Pos, 1, .1, .2, 0, 1, .2, 0.01, 5, ""}
|
||||
#{2, "m$(N)", 1, "motor $(N)", degrees, Pos, 1, .1, .2, 0, 1, .2, 0.01, 5, ""}
|
||||
}
|
6
motorSimApp/iocsh/Makefile
Normal file
6
motorSimApp/iocsh/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
TOP = ../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
IOCSH += EXAMPLE_motorSim.substitutions
|
||||
|
||||
include $(TOP)/configure/RULES
|
34
motorSimApp/iocsh/motorSim.iocsh
Normal file
34
motorSimApp/iocsh/motorSim.iocsh
Normal file
@ -0,0 +1,34 @@
|
||||
# ### motorSim.iocsh ###
|
||||
|
||||
#- ###################################################
|
||||
#- PREFIX - IOC Prefix
|
||||
#- INSTANCE - Instance name, used to create the low-level driver drvet name
|
||||
#- Combined with the controller number to create the asyn port name
|
||||
#-
|
||||
#- SUB - Optional: Subsitutions file (asyn_motor.db), Macros P, DTYP, PORT,
|
||||
#- DHLM, DLLM, and INIT will be predefined.
|
||||
#- Default: $(MOTOR)/iocsh/EXAMPLE_motorSim.substitutions
|
||||
#-
|
||||
#- CONTROLLER - Optional: Which controller is being configured
|
||||
#- Default: 0
|
||||
#-
|
||||
#- NUM_AXES - Optional: Number of axes on this controller
|
||||
#- Default: 1
|
||||
#-
|
||||
#- LOW_LIM - Optional: Low Limit
|
||||
#- Default: -32000
|
||||
#-
|
||||
#- HIGH_LIM - Optional: High Limit
|
||||
#- Default: 32000
|
||||
#-
|
||||
#- HOME_POS - Optional: Home position
|
||||
#- Default: 0
|
||||
#- ###################################################
|
||||
|
||||
# Create simulated motors: ( start card , start axis , low limit, high limit, home posn, # cards, # axes to setup)
|
||||
motorSimCreate($(CONTROLLER=0), 0, $(LOW_LIM=-32000), $(HIGH_LIM=32000), $(HOME_POS=0), 1, $(NUM_AXES=1))
|
||||
|
||||
# Setup the Asyn layer (portname, low-level driver drvet name, card, number of axes on card)
|
||||
drvAsynMotorConfigure("$(INSTANCE)$(CONTROLLER=0)", "$(INSTANCE)", $(CONTROLLER=0), $(NUM_AXES=1))
|
||||
|
||||
dbLoadTemplate("$(SUB=$(MOTOR)/iocsh/EXAMPLE_motorSim.substitutions)", "P=$(PREFIX), DTYP='asynMotor', PORT=$(INSTANCE)$(CONTROLLER=0), DHLM=$(HIGH_LIM=32000), DLLM=$(LOW_LIM=-32000)")
|
63
motorSimApp/src/Makefile
Normal file
63
motorSimApp/src/Makefile
Normal file
@ -0,0 +1,63 @@
|
||||
TOP=../..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
#----------------------------------------
|
||||
# ADD MACRO DEFINITIONS AFTER THIS LINE
|
||||
#=============================
|
||||
|
||||
#==================================================
|
||||
# Build an IOC support library
|
||||
|
||||
LIBRARY_IOC += motorSimSupport
|
||||
|
||||
# motorRecord.h will be created from motorRecord.dbd
|
||||
# install devMotorSoft.dbd into <top>/dbd
|
||||
DBD += motorSimSupport.dbd
|
||||
|
||||
# The following are compiled and added to the Support library
|
||||
motorSimSupport_SRCS += route.c
|
||||
motorSimSupport_SRCS += devMotorSim.c
|
||||
motorSimSupport_SRCS += drvMotorSim.c
|
||||
motorSimSupport_SRCS += motorSimDriver.cpp
|
||||
motorSimSupport_SRCS += motorSimRegister.cc
|
||||
|
||||
motorSimSupport_LIBS += motor
|
||||
motorSimSupport_LIBS += asyn
|
||||
motorSimSupport_LIBS += $(EPICS_BASE_IOC_LIBS)
|
||||
|
||||
#=============================
|
||||
# build an ioc application
|
||||
|
||||
PROD_IOC = motorSim
|
||||
# motorSim.dbd will be created and installed
|
||||
DBD += motorSim.dbd
|
||||
|
||||
# motorSim.dbd will be made up from these files:
|
||||
motorSim_DBD += base.dbd
|
||||
motorSim_DBD += asyn.dbd
|
||||
motorSim_DBD += motorSupport.dbd
|
||||
motorSim_DBD += motorSimSupport.dbd
|
||||
|
||||
# <name>_registerRecordDeviceDriver.cpp will be created from <name>.dbd
|
||||
motorSim_SRCS += motorSim_registerRecordDeviceDriver.cpp
|
||||
motorSim_SRCS_DEFAULT += motorSimMain.cpp
|
||||
motorSim_SRCS_vxWorks += -nil-
|
||||
|
||||
# The following adds support from base/src/vxWorks
|
||||
motorSim_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary
|
||||
|
||||
motorSim_LIBS += motorSimSupport
|
||||
motorSim_LIBS += motor
|
||||
motorSim_LIBS += asyn
|
||||
|
||||
motorSim_LIBS += $(EPICS_BASE_IOC_LIBS)
|
||||
|
||||
#===========================
|
||||
|
||||
# SCRIPTS += motorSimTest.boot
|
||||
DB += motorSimTest.db
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
#----------------------------------------
|
||||
# ADD RULES AFTER THIS LINE
|
||||
|
331
motorSimApp/src/devMotorSim.c
Normal file
331
motorSimApp/src/devMotorSim.c
Normal file
@ -0,0 +1,331 @@
|
||||
/* devXxxSoft.c */
|
||||
/* Example device support module */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* The following is needed to compile against Base R3.16.1 without a warning */
|
||||
#define USE_TYPED_RSET
|
||||
|
||||
#include "epicsFindSymbol.h"
|
||||
#include "dbAccess.h"
|
||||
#include "recGbl.h"
|
||||
#include "registryDriverSupport.h"
|
||||
#include "drvSup.h"
|
||||
/* #include "callback.h" */
|
||||
|
||||
#include "motorRecord.h"
|
||||
#include "motor.h"
|
||||
#include "epicsExport.h"
|
||||
#include "motor_interface.h"
|
||||
|
||||
/*Create the dset for devMotor */
|
||||
static long init_record(struct motorRecord *);
|
||||
static CALLBACK_VALUE update_values(struct motorRecord *);
|
||||
static long start_trans(struct motorRecord *);
|
||||
static RTN_STATUS build_trans( motor_cmnd, double *, struct motorRecord *);
|
||||
static RTN_STATUS end_trans(struct motorRecord *);
|
||||
|
||||
struct motor_dset devMotorSim={ { 8,
|
||||
NULL,
|
||||
NULL,
|
||||
(DEVSUPFUN) init_record,
|
||||
NULL },
|
||||
update_values,
|
||||
start_trans,
|
||||
build_trans,
|
||||
end_trans };
|
||||
|
||||
epicsExportAddress(dset,devMotorSim);
|
||||
|
||||
#define SET_BIT(val,mask,set) ((set)? ((val) | (mask)): ((val) & ~(mask)))
|
||||
|
||||
/**\struct motorStatus_t
|
||||
|
||||
This structure is returned by motorAxisGetStatus and contains all
|
||||
the current information required by the motor record to indicate
|
||||
current motor status. It should probably be extended to include
|
||||
all of status information which might be useful (i.e. current
|
||||
values of all parameter values that can be set).
|
||||
|
||||
Note that the updateCount can be used to indicate whether any
|
||||
parameters have been changes since the last call to the motorAxisGetStatus routine.
|
||||
|
||||
*/
|
||||
typedef struct motorStatus_t
|
||||
{
|
||||
epicsUInt32 status; /**< bit mask of errors and other binary information. The bit positions are in motor.h */
|
||||
epicsInt32 position; /**< Current motor position in motor steps (if not servoing) or demand position (if servoing) */
|
||||
epicsInt32 encoder_position; /**< Current motor position in encoder units (only available if a servo system). */
|
||||
} motorStatus_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
motorAxisDrvSET_t * drvset;
|
||||
AXIS_HDL pAxis;
|
||||
struct motorRecord * pRec;
|
||||
motorStatus_t status;
|
||||
double min_vel;
|
||||
double max_vel;
|
||||
double acc;
|
||||
motor_cmnd move_cmd;
|
||||
double param;
|
||||
int needUpdate;
|
||||
} motorPrvt_t;
|
||||
|
||||
|
||||
|
||||
void motor_callback( void * param, unsigned int nReasons, unsigned int * reasons )
|
||||
{
|
||||
struct motorRecord * pRec = (struct motorRecord *) param;
|
||||
motorPrvt_t * pPrvt = (motorPrvt_t *) pRec->dpvt;
|
||||
AXIS_HDL pAxis = pPrvt->pAxis;
|
||||
motorStatus_t * status = &(pPrvt->status);
|
||||
int i;
|
||||
|
||||
for ( i = 0; i < nReasons; i++ )
|
||||
{
|
||||
switch (reasons[i])
|
||||
{
|
||||
case motorAxisEncoderPosn:
|
||||
pPrvt->drvset->getInteger( pAxis, reasons[i], &(status->encoder_position) );
|
||||
break;
|
||||
case motorAxisPosition:
|
||||
pPrvt->drvset->getInteger( pAxis, reasons[i], &(status->position) );
|
||||
break;
|
||||
case motorAxisDirection:
|
||||
case motorAxisDone:
|
||||
case motorAxisHighHardLimit:
|
||||
case motorAxisHomeSignal:
|
||||
case motorAxisSlip:
|
||||
case motorAxisPowerOn:
|
||||
case motorAxisFollowingError:
|
||||
case motorAxisHomeEncoder:
|
||||
case motorAxisHasEncoder:
|
||||
case motorAxisProblem:
|
||||
case motorAxisMoving:
|
||||
case motorAxisHasClosedLoop:
|
||||
case motorAxisCommError:
|
||||
case motorAxisLowHardLimit:
|
||||
{
|
||||
int flag;
|
||||
int mask = (0x1<<(reasons[i]-motorAxisDirection));
|
||||
|
||||
pPrvt->drvset->getInteger( pAxis, reasons[i], &flag );
|
||||
status->status = SET_BIT( status->status, mask, flag );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (nReasons > 0)
|
||||
{
|
||||
pPrvt->needUpdate = 1;
|
||||
scanOnce( (struct dbCommon *)pRec );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static long init_record(struct motorRecord * pRec )
|
||||
{
|
||||
if (pRec->out.type == VME_IO)
|
||||
{
|
||||
/* I should extract the first word of the parameter as the driver support entry table name,
|
||||
and pass the rest to the driver, but I am a bit lazy at the moment */
|
||||
motorAxisDrvSET_t * drvset = (motorAxisDrvSET_t *) registryDriverSupportFind( pRec->out.value.vmeio.parm );
|
||||
|
||||
if (drvset != NULL &&
|
||||
drvset->open != NULL )
|
||||
{
|
||||
AXIS_HDL axis = (*drvset->open)( pRec->out.value.vmeio.card,
|
||||
pRec->out.value.vmeio.signal,
|
||||
pRec->out.value.vmeio.parm );
|
||||
if (axis != NULL)
|
||||
{
|
||||
pRec->dpvt = calloc( 1, sizeof( motorPrvt_t ) );
|
||||
if (pRec->dpvt != NULL)
|
||||
{
|
||||
int i;
|
||||
motorPrvt_t * pPrvt = (motorPrvt_t *) pRec->dpvt;
|
||||
|
||||
pPrvt->drvset = drvset;
|
||||
pPrvt->pAxis = axis;
|
||||
pPrvt->pRec = pRec;
|
||||
pPrvt->move_cmd = -1;
|
||||
pPrvt->drvset->getInteger( axis, motorAxisEncoderPosn, &(pPrvt->status.encoder_position) );
|
||||
pPrvt->drvset->getInteger( axis, motorAxisPosition, &(pPrvt->status.position) );
|
||||
|
||||
/* Set the status bits */
|
||||
for ( i = motorAxisDirection; i <= motorAxisLowHardLimit; i++ )
|
||||
{
|
||||
/* Set the default to be zero for unsupported flags */
|
||||
int flag=0;
|
||||
int mask = (0x1<<(i-motorAxisDirection));
|
||||
|
||||
pPrvt->drvset->getInteger( axis, i, &flag );
|
||||
pPrvt->status.status = SET_BIT( pPrvt->status.status, mask, flag );
|
||||
}
|
||||
|
||||
(*drvset->setCallback)( axis, motor_callback, (void *) pRec );
|
||||
}
|
||||
else
|
||||
{
|
||||
if (drvset->close) (*drvset->close)( axis );
|
||||
recGblRecordError(S_drv_noDrvSup,(void *)pRec,
|
||||
"devMotor (init_record) cannot open driver support");
|
||||
return S_db_noMemory;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
recGblRecordError(S_drv_noDrvSup,(void *)pRec,
|
||||
"devMotor (init_record) cannot open device support");
|
||||
return S_db_noSupport;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
recGblRecordError(S_drv_noDrvet,(void *)pRec,
|
||||
"devMotor (init_record) cannot find device support entry table");
|
||||
return S_db_noSupport;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
recGblRecordError(S_db_badField,(void *)pRec,
|
||||
"devMotor (init_record) Illegal INP field");
|
||||
return(S_db_badField);
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
CALLBACK_VALUE update_values(struct motorRecord * pRec)
|
||||
{
|
||||
motorPrvt_t * pPrvt = (motorPrvt_t *) pRec->dpvt;
|
||||
motorStatus_t stat = pPrvt->status;
|
||||
CALLBACK_VALUE rc;
|
||||
|
||||
rc = NOTHING_DONE;
|
||||
|
||||
if ( pPrvt->needUpdate )
|
||||
{
|
||||
pRec->rmp = stat.position;
|
||||
pRec->rep = stat.encoder_position;
|
||||
/* pRec->rvel = ptrans->vel; */
|
||||
pRec->msta = stat.status;
|
||||
rc = CALLBACK_DATA;
|
||||
pPrvt->needUpdate = 0;
|
||||
}
|
||||
return (rc);
|
||||
}
|
||||
|
||||
static long start_trans(struct motorRecord * pRec )
|
||||
{
|
||||
return(OK);
|
||||
}
|
||||
|
||||
static RTN_STATUS build_trans( motor_cmnd command,
|
||||
double * param,
|
||||
struct motorRecord * pRec )
|
||||
{
|
||||
RTN_STATUS status = OK;
|
||||
motorPrvt_t * pPrvt = (motorPrvt_t *) pRec->dpvt;
|
||||
|
||||
switch ( command )
|
||||
{
|
||||
case MOVE_ABS:
|
||||
case MOVE_REL:
|
||||
case HOME_FOR:
|
||||
case HOME_REV:
|
||||
pPrvt->move_cmd = command;
|
||||
pPrvt->param = *param;
|
||||
break;
|
||||
case LOAD_POS:
|
||||
status = (*pPrvt->drvset->setDouble)(pPrvt->pAxis, motorAxisPosition, *param );
|
||||
break;
|
||||
case SET_VEL_BASE:
|
||||
pPrvt->min_vel = *param;
|
||||
break;
|
||||
case SET_VELOCITY:
|
||||
pPrvt->max_vel = *param;
|
||||
break;
|
||||
case SET_ACCEL:
|
||||
pPrvt->acc = *param;
|
||||
break;
|
||||
case GO:
|
||||
switch (pPrvt->move_cmd)
|
||||
{
|
||||
case MOVE_ABS:
|
||||
status = (*pPrvt->drvset->move)(pPrvt->pAxis, pPrvt->param, 0,
|
||||
pPrvt->min_vel, pPrvt->max_vel, pPrvt->acc);
|
||||
break;
|
||||
case MOVE_REL:
|
||||
status = (*pPrvt->drvset->move)(pPrvt->pAxis, pPrvt->param, 1,
|
||||
pPrvt->min_vel, pPrvt->max_vel, pPrvt->acc);
|
||||
break;
|
||||
case HOME_FOR:
|
||||
status = (*pPrvt->drvset->home)(pPrvt->pAxis, pPrvt->min_vel, pPrvt->max_vel, pPrvt->acc, 1);
|
||||
break;
|
||||
case HOME_REV:
|
||||
status = (*pPrvt->drvset->home)(pPrvt->pAxis, pPrvt->min_vel, pPrvt->max_vel, pPrvt->acc, 0);
|
||||
break;
|
||||
default:
|
||||
status = ERROR;
|
||||
}
|
||||
pPrvt->move_cmd = -1;
|
||||
break;
|
||||
case SET_ENC_RATIO:
|
||||
status = (*pPrvt->drvset->setDouble)(pPrvt->pAxis, motorAxisEncoderRatio, param[0]/param[1] );
|
||||
break;
|
||||
case GET_INFO:
|
||||
pPrvt->needUpdate = 1;
|
||||
break;
|
||||
case STOP_AXIS:
|
||||
status = (*pPrvt->drvset->stop)(pPrvt->pAxis, pPrvt->acc );
|
||||
break;
|
||||
case JOG:
|
||||
status = (*pPrvt->drvset->velocityMove)(pPrvt->pAxis, pPrvt->min_vel, *param, pPrvt->acc);
|
||||
break;
|
||||
case JOG_VELOCITY:
|
||||
status = (*pPrvt->drvset->velocityMove)(pPrvt->pAxis, pPrvt->min_vel, *param, pPrvt->acc);
|
||||
break;
|
||||
case SET_PGAIN:
|
||||
status = (*pPrvt->drvset->setDouble)(pPrvt->pAxis, motorAxisPGain, *param );
|
||||
break;
|
||||
case SET_IGAIN:
|
||||
status = (*pPrvt->drvset->setDouble)(pPrvt->pAxis, motorAxisIGain, *param );
|
||||
break;
|
||||
case SET_DGAIN:
|
||||
status = (*pPrvt->drvset->setDouble)(pPrvt->pAxis, motorAxisIGain, *param );
|
||||
break;
|
||||
case ENABLE_TORQUE:
|
||||
status = (*pPrvt->drvset->setInteger)(pPrvt->pAxis, motorAxisClosedLoop, 1 );
|
||||
break;
|
||||
case DISABL_TORQUE:
|
||||
status = (*pPrvt->drvset->setInteger)(pPrvt->pAxis, motorAxisClosedLoop, 0 );
|
||||
break;
|
||||
case SET_HIGH_LIMIT:
|
||||
status = (*pPrvt->drvset->setDouble)(pPrvt->pAxis, motorAxisHighLimit, *param );
|
||||
break;
|
||||
case SET_LOW_LIMIT:
|
||||
status = (*pPrvt->drvset->setDouble)(pPrvt->pAxis, motorAxisLowLimit, *param );
|
||||
break;
|
||||
default:
|
||||
status = ERROR;
|
||||
}
|
||||
|
||||
return(status);
|
||||
}
|
||||
|
||||
static RTN_STATUS end_trans(struct motorRecord * pRec )
|
||||
{
|
||||
/* motorPrvt_t * pPrvt = (motorPrvt_t *) pRec->dpvt; */
|
||||
|
||||
/* callbackRequestProcessCallback( &(pPrvt->callback), priorityLow, pRec ); */
|
||||
return(OK);
|
||||
}
|
822
motorSimApp/src/drvMotorSim.c
Normal file
822
motorSimApp/src/drvMotorSim.c
Normal file
@ -0,0 +1,822 @@
|
||||
/*
|
||||
FILENAME... drvMotorSim.c
|
||||
USAGE... Simulated Motor Support.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
* Modification Log:
|
||||
* -----------------
|
||||
* 2006-05-06 npr Added prolog
|
||||
* 2009-02-11 rls lock/unlock motorAxisSetDouble().
|
||||
* 2009-06-18 rls - Matthew Pearson's fix for record seeing motorAxisDone True
|
||||
* on 1st status update after a move; set motorAxisDone False
|
||||
* in motorAxisDrvSET_t functions that start motion
|
||||
* (motorAxisHome, motorAxisMove, motorAxisVelocityMove) and
|
||||
* force a status update with a call to callCallback().
|
||||
* - Matthew Pearson added deferred move support.
|
||||
* 2010-10-05 rls - MP's fix for deferred moves broken in drvMotorSim.
|
||||
* 2012-10-09 rls - Added motorAxisforceCallback to support motor record
|
||||
* GET_INFO commands.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "drvMotorSim.h"
|
||||
#include "paramLib.h"
|
||||
|
||||
#include "epicsFindSymbol.h"
|
||||
#include "epicsTime.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "ellLib.h"
|
||||
|
||||
#include "drvSup.h"
|
||||
#include "epicsExport.h"
|
||||
#define DEFINE_MOTOR_PROTOTYPES 1
|
||||
#include "motor_interface.h"
|
||||
|
||||
#include "route.h"
|
||||
|
||||
motorAxisDrvSET_t motorSim =
|
||||
{
|
||||
14,
|
||||
motorAxisReport, /**< Standard EPICS driver report function (optional) */
|
||||
motorAxisInit, /**< Standard EPICS dirver initialisation function (optional) */
|
||||
motorAxisSetLog, /**< Defines an external logging function (optional) */
|
||||
motorAxisOpen, /**< Driver open function */
|
||||
motorAxisClose, /**< Driver close function */
|
||||
motorAxisSetCallback, /**< Provides a callback function the driver can call when the status updates */
|
||||
motorAxisSetDouble, /**< Pointer to function to set a double value */
|
||||
motorAxisSetInteger, /**< Pointer to function to set an integer value */
|
||||
motorAxisGetDouble, /**< Pointer to function to get a double value */
|
||||
motorAxisGetInteger, /**< Pointer to function to get an integer value */
|
||||
motorAxisHome, /**< Pointer to function to execute a more to reference or home */
|
||||
motorAxisMove, /**< Pointer to function to execute a position move */
|
||||
motorAxisVelocityMove, /**< Pointer to function to execute a velocity mode move */
|
||||
motorAxisStop, /**< Pointer to function to stop motion */
|
||||
motorAxisforceCallback, /**< Pointer to function to request a poller status update */
|
||||
motorAxisProfileMove, /**< Pointer to function to execute a profile move */
|
||||
motorAxisTriggerProfile /**< Pointer to function to trigger a profile move */
|
||||
};
|
||||
|
||||
epicsExportAddress(drvet, motorSim);
|
||||
|
||||
typedef enum { none, positionMove, velocityMove, homeReverseMove, homeForwardsMove } moveType;
|
||||
|
||||
/* typedef struct motorAxis * AXIS_ID; */
|
||||
|
||||
typedef struct drvSim * DRVSIM_ID;
|
||||
typedef struct drvSim
|
||||
{
|
||||
AXIS_HDL pFirst;
|
||||
epicsThreadId motorThread;
|
||||
motorAxisLogFunc print;
|
||||
void * logParam;
|
||||
epicsTimeStamp now;
|
||||
int movesDeferred;
|
||||
int nAxes;
|
||||
} motorSim_t;
|
||||
|
||||
typedef struct motorAxisHandle
|
||||
{
|
||||
AXIS_HDL pNext;
|
||||
AXIS_HDL pPrev;
|
||||
int card;
|
||||
int axis;
|
||||
ROUTE_ID route;
|
||||
PARAMS params;
|
||||
route_reroute_t reroute;
|
||||
route_demand_t endpoint;
|
||||
route_demand_t nextpoint;
|
||||
double hiHardLimit;
|
||||
double lowHardLimit;
|
||||
double enc_offset;
|
||||
double home;
|
||||
int homing;
|
||||
motorAxisLogFunc print;
|
||||
void * logParam;
|
||||
epicsTimeStamp tLast;
|
||||
epicsMutexId axisMutex;
|
||||
double deferred_position;
|
||||
int deferred_move;
|
||||
int deferred_relative;
|
||||
DRVSIM_ID pDrv;
|
||||
} motorAxis;
|
||||
|
||||
|
||||
|
||||
static int motorSimLogMsg( void * param, const motorAxisLogMask_t logMask, const char *pFormat, ...);
|
||||
#define TRACE_FLOW motorAxisTraceFlow
|
||||
#define TRACE_ERROR motorAxisTraceError
|
||||
|
||||
static motorSim_t drv={ NULL, NULL, motorSimLogMsg, NULL, { 0, 0 } };
|
||||
|
||||
#define MAX(a,b) ((a)>(b)? (a): (b))
|
||||
#define MIN(a,b) ((a)<(b)? (a): (b))
|
||||
#define DELTA 0.1
|
||||
|
||||
/*Deferred moves functions.*/
|
||||
static int processDeferredMoves(const motorSim_t * pDrv);
|
||||
|
||||
static void motorProcTask(motorSim_t *);
|
||||
|
||||
static void motorAxisReportAxis( AXIS_HDL pAxis, int level )
|
||||
{
|
||||
printf( "Found driver for motorSim card %d, axis %d, mutex %p\n", pAxis->card, pAxis->axis, pAxis->axisMutex );
|
||||
|
||||
if (level > 0)
|
||||
{
|
||||
double lowSoftLimit=0.0;
|
||||
double hiSoftLimit=0.0;
|
||||
|
||||
printf( "Current position = %f, velocity = %f at current time: %f\n",
|
||||
pAxis->nextpoint.axis[0].p,
|
||||
pAxis->nextpoint.axis[0].v,
|
||||
pAxis->nextpoint.T );
|
||||
printf( "Destination posn = %f, velocity = %f at desination time: %f\n",
|
||||
pAxis->endpoint.axis[0].p,
|
||||
pAxis->endpoint.axis[0].v,
|
||||
pAxis->endpoint.T );
|
||||
|
||||
printf( "Hard limits: %f, %f\n", pAxis->lowHardLimit, pAxis->hiHardLimit );
|
||||
motorParam->getDouble( pAxis->params, motorAxisHighLimit, &hiSoftLimit );
|
||||
motorParam->getDouble( pAxis->params, motorAxisLowLimit, &lowSoftLimit );
|
||||
printf( "Soft limits: %f, %f\n", lowSoftLimit, hiSoftLimit );
|
||||
|
||||
if (pAxis->homing) printf( "Currently homing axis\n" );
|
||||
|
||||
motorParam->dump( pAxis->params );
|
||||
}
|
||||
}
|
||||
|
||||
static void motorAxisReport( int level )
|
||||
{
|
||||
AXIS_HDL pAxis;
|
||||
|
||||
for ( pAxis=drv.pFirst; pAxis != NULL; pAxis = pAxis->pNext ) motorAxisReportAxis( pAxis, level );
|
||||
}
|
||||
|
||||
|
||||
static int motorAxisInit( void )
|
||||
{
|
||||
return MOTOR_AXIS_OK;
|
||||
}
|
||||
|
||||
static int motorAxisSetLog( AXIS_HDL pAxis, motorAxisLogFunc logFunc, void * param )
|
||||
{
|
||||
if (pAxis == NULL)
|
||||
{
|
||||
if (logFunc == NULL)
|
||||
{
|
||||
drv.print=motorSimLogMsg;
|
||||
drv.logParam = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
drv.print=logFunc;
|
||||
drv.logParam = param;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (logFunc == NULL)
|
||||
{
|
||||
pAxis->print=motorSimLogMsg;
|
||||
pAxis->logParam = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
pAxis->print=logFunc;
|
||||
pAxis->logParam = param;
|
||||
}
|
||||
}
|
||||
return MOTOR_AXIS_OK;
|
||||
}
|
||||
|
||||
static AXIS_HDL motorAxisOpen( int card, int axis, char * param )
|
||||
{
|
||||
AXIS_HDL pAxis;
|
||||
|
||||
for ( pAxis=drv.pFirst;
|
||||
pAxis != NULL && (card != pAxis->card || axis != pAxis->axis );
|
||||
pAxis = pAxis->pNext ) {}
|
||||
|
||||
return pAxis;
|
||||
}
|
||||
|
||||
static int motorAxisClose( AXIS_HDL pAxis )
|
||||
{
|
||||
return MOTOR_AXIS_OK;
|
||||
}
|
||||
|
||||
static int motorAxisGetInteger( AXIS_HDL pAxis, motorAxisParam_t function, int * value )
|
||||
{
|
||||
if (pAxis == NULL) return MOTOR_AXIS_ERROR;
|
||||
else
|
||||
{
|
||||
switch (function) {
|
||||
case motorAxisDeferMoves:
|
||||
*value = pAxis->pDrv->movesDeferred;
|
||||
return MOTOR_AXIS_OK;
|
||||
default:
|
||||
return motorParam->getInteger( pAxis->params, (paramIndex) function, value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int motorAxisGetDouble( AXIS_HDL pAxis, motorAxisParam_t function, double * value )
|
||||
{
|
||||
if (pAxis == NULL) return MOTOR_AXIS_ERROR;
|
||||
else
|
||||
{
|
||||
switch (function) {
|
||||
case motorAxisDeferMoves:
|
||||
*value = (double)pAxis->pDrv->movesDeferred;
|
||||
return MOTOR_AXIS_OK;
|
||||
default:
|
||||
return motorParam->getDouble( pAxis->params, (paramIndex) function, value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int motorAxisSetCallback( AXIS_HDL pAxis, motorAxisCallbackFunc callback, void * param )
|
||||
{
|
||||
if (pAxis == NULL) return MOTOR_AXIS_ERROR;
|
||||
else
|
||||
{
|
||||
return motorParam->setCallback( pAxis->params, callback, param );
|
||||
}
|
||||
}
|
||||
|
||||
static int processDeferredMoves(const motorSim_t * pDrv)
|
||||
{
|
||||
int status = MOTOR_AXIS_ERROR;
|
||||
double position = 0.0;
|
||||
AXIS_HDL pAxis = NULL;
|
||||
|
||||
for ( pAxis = pDrv->pFirst; pAxis != NULL; pAxis = pAxis->pNext )
|
||||
{
|
||||
if (pAxis->deferred_move) {
|
||||
|
||||
position = pAxis->deferred_position;
|
||||
|
||||
/* Check to see if in hard limits */
|
||||
if ((pAxis->nextpoint.axis[0].p >= pAxis->hiHardLimit && position > pAxis->nextpoint.axis[0].p) ||
|
||||
(pAxis->nextpoint.axis[0].p <= pAxis->lowHardLimit && position < pAxis->nextpoint.axis[0].p) ) return MOTOR_AXIS_ERROR;
|
||||
else if (epicsMutexLock( pAxis->axisMutex ) == epicsMutexLockOK)
|
||||
{
|
||||
pAxis->endpoint.axis[0].p = position - pAxis->enc_offset;
|
||||
pAxis->endpoint.axis[0].v = 0.0;
|
||||
|
||||
motorParam->setInteger( pAxis->params, motorAxisDone, 0 );
|
||||
|
||||
pAxis->deferred_move = 0;
|
||||
epicsMutexUnlock( pAxis->axisMutex );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static int motorAxisSetDouble( AXIS_HDL pAxis, motorAxisParam_t function, double value )
|
||||
{
|
||||
int status = MOTOR_AXIS_OK;
|
||||
|
||||
if (pAxis == NULL) return MOTOR_AXIS_ERROR;
|
||||
else
|
||||
{
|
||||
epicsMutexLock(pAxis->axisMutex);
|
||||
switch (function)
|
||||
{
|
||||
case motorAxisPosition:
|
||||
{
|
||||
pAxis->enc_offset = value - pAxis->nextpoint.axis[0].p;
|
||||
pAxis->print( pAxis->logParam, TRACE_FLOW, "Set card %d, axis %d to position %f", pAxis->card, pAxis->axis, value );
|
||||
break;
|
||||
}
|
||||
case motorAxisResolution:
|
||||
{
|
||||
pAxis->print( pAxis->logParam, TRACE_FLOW, "Set card %d, axis %d resolution to %f", pAxis->card, pAxis->axis, value );
|
||||
break;
|
||||
}
|
||||
case motorAxisEncoderRatio:
|
||||
{
|
||||
pAxis->print( pAxis->logParam, TRACE_FLOW, "Set card %d, axis %d to enc. ratio to %f", pAxis->card, pAxis->axis, value );
|
||||
break;
|
||||
}
|
||||
case motorAxisLowLimit:
|
||||
{
|
||||
pAxis->print( pAxis->logParam, TRACE_FLOW, "Set card %d, axis %d low limit to %f", pAxis->card, pAxis->axis, value );
|
||||
break;
|
||||
}
|
||||
case motorAxisHighLimit:
|
||||
{
|
||||
pAxis->print( pAxis->logParam, TRACE_FLOW, "Set card %d, axis %d high limit to %f", pAxis->card, pAxis->axis, value );
|
||||
break;
|
||||
}
|
||||
case motorAxisPGain:
|
||||
{
|
||||
pAxis->print( pAxis->logParam, TRACE_FLOW, "Set card %d, axis %d pgain to %f", pAxis->card, pAxis->axis, value );
|
||||
break;
|
||||
}
|
||||
case motorAxisIGain:
|
||||
{
|
||||
pAxis->print( pAxis->logParam, TRACE_FLOW, "Set card %d, axis %d igain to %f", pAxis->card, pAxis->axis, value );
|
||||
break;
|
||||
}
|
||||
case motorAxisDGain:
|
||||
{
|
||||
pAxis->print( pAxis->logParam, TRACE_FLOW, "Set card %d, axis %d dgain to %f", pAxis->card, pAxis->axis, value );
|
||||
break;
|
||||
}
|
||||
case motorAxisClosedLoop:
|
||||
{
|
||||
pAxis->print( pAxis->logParam, TRACE_FLOW, "Set card %d, axis %d closed loop to %s", pAxis->card, pAxis->axis, (value!=0?"ON":"OFF") );
|
||||
break;
|
||||
}
|
||||
case motorAxisDeferMoves:
|
||||
{
|
||||
pAxis->print( pAxis->logParam, TRACE_FLOW,
|
||||
"%sing Deferred Move flag on PMAC card %d\n",
|
||||
value != 0.0?"Sett":"Clear",pAxis->card);
|
||||
if (value == 0.0 && pAxis->pDrv->movesDeferred != 0) {
|
||||
processDeferredMoves(pAxis->pDrv);
|
||||
}
|
||||
pAxis->pDrv->movesDeferred = (int)value;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
status = MOTOR_AXIS_ERROR;
|
||||
break;
|
||||
}
|
||||
if (status == MOTOR_AXIS_OK )
|
||||
{
|
||||
motorParam->setDouble(pAxis->params, function, value);
|
||||
motorParam->callCallback(pAxis->params);
|
||||
}
|
||||
epicsMutexUnlock(pAxis->axisMutex);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static int motorAxisSetInteger( AXIS_HDL pAxis, motorAxisParam_t function, int value )
|
||||
{
|
||||
int status = MOTOR_AXIS_OK;
|
||||
|
||||
if (pAxis == NULL)
|
||||
return (MOTOR_AXIS_ERROR);
|
||||
else
|
||||
{
|
||||
if (epicsMutexLock( pAxis->axisMutex ) == epicsMutexLockOK)
|
||||
{
|
||||
switch (function)
|
||||
{
|
||||
case motorAxisPosition:
|
||||
{
|
||||
pAxis->enc_offset = (double) value - pAxis->nextpoint.axis[0].p;
|
||||
pAxis->print( pAxis->logParam, TRACE_FLOW, "Set card %d, axis %d to position %d", pAxis->card, pAxis->axis, value );
|
||||
break;
|
||||
}
|
||||
case motorAxisLowLimit:
|
||||
{
|
||||
pAxis->print( pAxis->logParam, TRACE_FLOW, "Set card %d, axis %d low limit to %d", pAxis->card, pAxis->axis, value );
|
||||
break;
|
||||
}
|
||||
case motorAxisHighLimit:
|
||||
{
|
||||
pAxis->print( pAxis->logParam, TRACE_FLOW, "Set card %d, axis %d high limit to %d", pAxis->card, pAxis->axis, value );
|
||||
break;
|
||||
}
|
||||
case motorAxisClosedLoop:
|
||||
{
|
||||
pAxis->print( pAxis->logParam, TRACE_FLOW, "Set card %d, axis %d closed loop to %s", pAxis->card, pAxis->axis, (value?"ON":"OFF") );
|
||||
break;
|
||||
}
|
||||
case motorAxisDeferMoves:
|
||||
{
|
||||
pAxis->print( pAxis->logParam, TRACE_FLOW,
|
||||
"%sing Deferred Move flag on PMAC card %d\n",
|
||||
value != 0.0?"Sett":"Clear",pAxis->card);
|
||||
if (value == 0.0 && pAxis->pDrv->movesDeferred != 0)
|
||||
{
|
||||
processDeferredMoves(pAxis->pDrv);
|
||||
}
|
||||
pAxis->pDrv->movesDeferred = value;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
status = MOTOR_AXIS_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (status != MOTOR_AXIS_ERROR )
|
||||
{
|
||||
status = motorParam->setInteger( pAxis->params, function, value );
|
||||
motorParam->callCallback(pAxis->params);
|
||||
}
|
||||
epicsMutexUnlock(pAxis->axisMutex);
|
||||
}
|
||||
return (status);
|
||||
}
|
||||
}
|
||||
|
||||
static int motorAxisMove( AXIS_HDL pAxis, double position, int relative, double min_velocity, double max_velocity, double acceleration )
|
||||
{
|
||||
|
||||
if (pAxis == NULL) return MOTOR_AXIS_ERROR;
|
||||
else
|
||||
{
|
||||
if (relative) position += pAxis->endpoint.axis[0].p + pAxis->enc_offset;
|
||||
|
||||
/* Check to see if in hard limits */
|
||||
if ((pAxis->nextpoint.axis[0].p >= pAxis->hiHardLimit && position > pAxis->nextpoint.axis[0].p) ||
|
||||
(pAxis->nextpoint.axis[0].p <= pAxis->lowHardLimit && position < pAxis->nextpoint.axis[0].p) ) return MOTOR_AXIS_ERROR;
|
||||
else if (epicsMutexLock( pAxis->axisMutex ) == epicsMutexLockOK)
|
||||
{
|
||||
route_pars_t pars;
|
||||
|
||||
if (pAxis->pDrv->movesDeferred == 0) { /*Normal move.*/
|
||||
pAxis->endpoint.axis[0].p = position - pAxis->enc_offset;
|
||||
pAxis->endpoint.axis[0].v = 0.0;
|
||||
} else { /*Deferred moves.*/
|
||||
pAxis->deferred_position = position;
|
||||
pAxis->deferred_move = 1;
|
||||
pAxis->deferred_relative = relative;
|
||||
}
|
||||
routeGetParams( pAxis->route, &pars );
|
||||
if (max_velocity != 0) pars.axis[0].Vmax = fabs(max_velocity);
|
||||
if (acceleration != 0) pars.axis[0].Amax = fabs(acceleration);
|
||||
routeSetParams( pAxis->route, &pars );
|
||||
|
||||
motorParam->setInteger( pAxis->params, motorAxisDone, 0 );
|
||||
motorParam->callCallback( pAxis->params );
|
||||
epicsMutexUnlock( pAxis->axisMutex );
|
||||
|
||||
pAxis->print( pAxis->logParam, TRACE_FLOW, "Set card %d, axis %d move to %f, min vel=%f, max_vel=%f, accel=%f",
|
||||
pAxis->card, pAxis->axis, position, min_velocity, max_velocity, acceleration );
|
||||
}
|
||||
|
||||
}
|
||||
return MOTOR_AXIS_OK;
|
||||
}
|
||||
|
||||
static int motorAxisVelocity( AXIS_HDL pAxis, double velocity, double acceleration )
|
||||
{
|
||||
route_pars_t pars;
|
||||
double deltaV = velocity - pAxis->nextpoint.axis[0].v;
|
||||
|
||||
/* Check to see if in hard limits */
|
||||
if ((pAxis->nextpoint.axis[0].p > pAxis->hiHardLimit && velocity > 0) ||
|
||||
(pAxis->nextpoint.axis[0].p < pAxis->lowHardLimit && velocity < 0) ) return MOTOR_AXIS_ERROR;
|
||||
else
|
||||
{
|
||||
double time;
|
||||
|
||||
routeGetParams( pAxis->route, &pars );
|
||||
if (acceleration != 0) pars.axis[0].Amax = fabs(acceleration);
|
||||
routeSetParams( pAxis->route, &pars );
|
||||
|
||||
time = fabs( deltaV / pars.axis[0].Amax );
|
||||
|
||||
pAxis->endpoint.axis[0].v = velocity;
|
||||
pAxis->endpoint.axis[0].p = ( pAxis->nextpoint.axis[0].p +
|
||||
time * ( pAxis->nextpoint.axis[0].v + 0.5 * deltaV ));
|
||||
pAxis->reroute = ROUTE_NEW_ROUTE;
|
||||
}
|
||||
return MOTOR_AXIS_OK;
|
||||
}
|
||||
|
||||
static int motorAxisHome( AXIS_HDL pAxis, double min_velocity, double max_velocity, double acceleration, int forwards )
|
||||
{
|
||||
int status = MOTOR_AXIS_ERROR;
|
||||
|
||||
if (pAxis == NULL) status = MOTOR_AXIS_ERROR;
|
||||
else
|
||||
{
|
||||
if (epicsMutexLock( pAxis->axisMutex ) == epicsMutexLockOK) {
|
||||
status = motorAxisVelocity( pAxis, (forwards? max_velocity: -max_velocity), acceleration );
|
||||
pAxis->homing = 1;
|
||||
motorParam->setInteger( pAxis->params, motorAxisDone, 0 );
|
||||
motorParam->callCallback( pAxis->params );
|
||||
epicsMutexUnlock( pAxis->axisMutex );
|
||||
pAxis->print( pAxis->logParam, TRACE_FLOW, "Set card %d, axis %d to home %s, min vel=%f, max_vel=%f, accel=%f",
|
||||
pAxis->card, pAxis->axis, (forwards?"FORWARDS":"REVERSE"), min_velocity, max_velocity, acceleration );
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static int motorAxisVelocityMove( AXIS_HDL pAxis, double min_velocity, double velocity, double acceleration )
|
||||
{
|
||||
int status = MOTOR_AXIS_ERROR;
|
||||
|
||||
if (pAxis == NULL) status = MOTOR_AXIS_ERROR;
|
||||
else
|
||||
{
|
||||
if (epicsMutexLock( pAxis->axisMutex ) == epicsMutexLockOK)
|
||||
{
|
||||
status = motorAxisVelocity( pAxis, velocity, acceleration );
|
||||
motorParam->setInteger( pAxis->params, motorAxisDone, 0 );
|
||||
motorParam->callCallback( pAxis->params );
|
||||
epicsMutexUnlock( pAxis->axisMutex );
|
||||
pAxis->print( pAxis->logParam, TRACE_FLOW, "Set card %d, axis %d move with velocity of %f, accel=%f",
|
||||
pAxis->card, pAxis->axis, velocity, acceleration );
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static int motorAxisProfileMove( AXIS_HDL pAxis, int npoints, double positions[], double times[], int relative, int trigger )
|
||||
{
|
||||
return MOTOR_AXIS_ERROR;
|
||||
}
|
||||
|
||||
static int motorAxisTriggerProfile( AXIS_HDL pAxis )
|
||||
{
|
||||
return MOTOR_AXIS_ERROR;
|
||||
}
|
||||
|
||||
static int motorAxisStop( AXIS_HDL pAxis, double acceleration )
|
||||
{
|
||||
if (pAxis == NULL) return MOTOR_AXIS_ERROR;
|
||||
else
|
||||
{
|
||||
if (epicsMutexLock( pAxis->axisMutex ) == epicsMutexLockOK) {
|
||||
motorAxisVelocity( pAxis, 0.0, acceleration );
|
||||
pAxis->deferred_move = 0;
|
||||
epicsMutexUnlock( pAxis->axisMutex );
|
||||
|
||||
pAxis->print( pAxis->logParam, TRACE_FLOW, "Set card %d, axis %d to stop with accel=%f",
|
||||
pAxis->card, pAxis->axis, acceleration );
|
||||
}
|
||||
|
||||
}
|
||||
return MOTOR_AXIS_OK;
|
||||
}
|
||||
|
||||
static int motorAxisforceCallback(AXIS_HDL pAxis)
|
||||
{
|
||||
if (pAxis == NULL)
|
||||
return(MOTOR_AXIS_ERROR);
|
||||
|
||||
/* Force a status update. */
|
||||
motorParam->forceCallback(pAxis->params);
|
||||
motorProcTask(&drv);
|
||||
return(MOTOR_AXIS_OK);
|
||||
}
|
||||
|
||||
|
||||
/**\defgroup motorSimTask Routines to implement the motor axis simulation task
|
||||
@{
|
||||
*/
|
||||
|
||||
/** Process one iteration of an axis
|
||||
|
||||
This routine takes a single axis and propogates its motion forward a given amount
|
||||
of time.
|
||||
|
||||
\param pAxis [in] Pointer to axis information.
|
||||
\param delta [in] Time in seconds to propogate motion forwards.
|
||||
|
||||
\return Integer indicating 0 (MOTOR_AXIS_OK) for success or non-zero for failure.
|
||||
*/
|
||||
|
||||
static void motorSimProcess( AXIS_HDL pAxis, double delta )
|
||||
{
|
||||
double lastpos = pAxis->nextpoint.axis[0].p;
|
||||
int done = 0;
|
||||
|
||||
pAxis->nextpoint.T += delta;
|
||||
routeFind( pAxis->route, pAxis->reroute, &(pAxis->endpoint), &(pAxis->nextpoint) );
|
||||
/* if (pAxis->reroute == ROUTE_NEW_ROUTE) routePrint( pAxis->route, pAxis->reroute, &(pAxis->endpoint), &(pAxis->nextpoint), stdout ); */
|
||||
pAxis->reroute = ROUTE_CALC_ROUTE;
|
||||
|
||||
/* No, do a limits check */
|
||||
if (pAxis->homing &&
|
||||
((lastpos - pAxis->home) * (pAxis->nextpoint.axis[0].p - pAxis->home)) <= 0)
|
||||
{
|
||||
/* Homing and have crossed the home sensor - return to home */
|
||||
pAxis->homing = 0;
|
||||
pAxis->reroute = ROUTE_NEW_ROUTE;
|
||||
pAxis->endpoint.axis[0].p = pAxis->home;
|
||||
pAxis->endpoint.axis[0].v = 0.0;
|
||||
}
|
||||
if ( pAxis->nextpoint.axis[0].p > pAxis->hiHardLimit && pAxis->nextpoint.axis[0].v > 0 )
|
||||
{
|
||||
if (pAxis->homing) motorAxisVelocity( pAxis, -pAxis->endpoint.axis[0].v, 0.0 );
|
||||
else
|
||||
{
|
||||
pAxis->reroute = ROUTE_NEW_ROUTE;
|
||||
pAxis->endpoint.axis[0].p = pAxis->hiHardLimit;
|
||||
pAxis->endpoint.axis[0].v = 0.0;
|
||||
}
|
||||
}
|
||||
else if (pAxis->nextpoint.axis[0].p < pAxis->lowHardLimit && pAxis->nextpoint.axis[0].v < 0)
|
||||
{
|
||||
if (pAxis->homing) motorAxisVelocity( pAxis, -pAxis->endpoint.axis[0].v, 0.0 );
|
||||
else
|
||||
{
|
||||
pAxis->reroute = ROUTE_NEW_ROUTE;
|
||||
pAxis->endpoint.axis[0].p = pAxis->lowHardLimit;
|
||||
pAxis->endpoint.axis[0].v = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
if (pAxis->nextpoint.axis[0].v == 0) {
|
||||
if (!pAxis->deferred_move) {
|
||||
done = 1;
|
||||
}
|
||||
} else {
|
||||
done = 0;
|
||||
}
|
||||
|
||||
motorParam->setDouble( pAxis->params, motorAxisPosition, (pAxis->nextpoint.axis[0].p+pAxis->enc_offset) );
|
||||
motorParam->setDouble( pAxis->params, motorAxisEncoderPosn, (pAxis->nextpoint.axis[0].p+pAxis->enc_offset) );
|
||||
motorParam->setInteger( pAxis->params, motorAxisDirection, (pAxis->nextpoint.axis[0].v > 0) );
|
||||
motorParam->setInteger( pAxis->params, motorAxisDone, done );
|
||||
motorParam->setInteger( pAxis->params, motorAxisHighHardLimit, (pAxis->nextpoint.axis[0].p >= pAxis->hiHardLimit) );
|
||||
motorParam->setInteger( pAxis->params, motorAxisHomeSignal, (pAxis->nextpoint.axis[0].p == pAxis->home) );
|
||||
motorParam->setInteger( pAxis->params, motorAxisMoving, !done );
|
||||
motorParam->setInteger( pAxis->params, motorAxisLowHardLimit, (pAxis->nextpoint.axis[0].p <= pAxis->lowHardLimit) );
|
||||
}
|
||||
|
||||
|
||||
static void motorProcTask( motorSim_t *pDrv)
|
||||
{
|
||||
epicsTimeStamp now;
|
||||
double delta;
|
||||
AXIS_HDL pAxis;
|
||||
|
||||
/* Get a new timestamp */
|
||||
epicsTimeGetCurrent( &now );
|
||||
delta = epicsTimeDiffInSeconds( &now, &(pDrv->now) );
|
||||
pDrv->now = now;
|
||||
|
||||
if ( delta > (DELTA/4.0) && delta <= (4.0*DELTA) )
|
||||
{
|
||||
/* A reasonable time has elapsed, it's not a time step in the clock */
|
||||
|
||||
for (pAxis = pDrv->pFirst; pAxis != NULL; pAxis = pAxis->pNext )
|
||||
{
|
||||
if (epicsMutexLock( pAxis->axisMutex ) == epicsMutexLockOK)
|
||||
{
|
||||
motorSimProcess( pAxis, delta );
|
||||
motorParam->callCallback( pAxis->params );
|
||||
epicsMutexUnlock( pAxis->axisMutex );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void motorSimTask( motorSim_t * pDrv )
|
||||
{
|
||||
while ( 1 )
|
||||
{
|
||||
motorProcTask(pDrv);
|
||||
epicsThreadSleep( DELTA );
|
||||
}
|
||||
}
|
||||
|
||||
static int motorSimCreateAxis( motorSim_t * pDrv, int card, int axis, double lowLimit, double hiLimit, double home, double start )
|
||||
{
|
||||
AXIS_HDL pAxis;
|
||||
AXIS_HDL * ppLast = &(pDrv->pFirst);
|
||||
start=0;
|
||||
|
||||
for ( pAxis = pDrv->pFirst;
|
||||
pAxis != NULL &&
|
||||
! ((pAxis->card == card) && (pAxis->axis == axis));
|
||||
pAxis = pAxis->pNext )
|
||||
{
|
||||
ppLast = &(pAxis->pNext);
|
||||
}
|
||||
|
||||
if ( pAxis == NULL)
|
||||
{
|
||||
pAxis = (AXIS_HDL) calloc( 1, sizeof(motorAxis) );
|
||||
if (pAxis != NULL)
|
||||
{
|
||||
route_pars_t pars;
|
||||
|
||||
pAxis->pDrv = pDrv;
|
||||
|
||||
pars.numRoutedAxes = 1;
|
||||
pars.routedAxisList[0] = 1;
|
||||
pars.Tsync = 0.0;
|
||||
pars.Tcoast = 0.0;
|
||||
pars.axis[0].Amax = 1.0;
|
||||
pars.axis[0].Vmax = 1.0;
|
||||
|
||||
pAxis->endpoint.T = 0;
|
||||
pAxis->endpoint.axis[0].p = start;
|
||||
pAxis->endpoint.axis[0].v = 0;
|
||||
pAxis->nextpoint.axis[0].p = start;
|
||||
|
||||
if ((pAxis->route = routeNew( &(pAxis->endpoint), &pars )) != NULL &&
|
||||
(pAxis->params = motorParam->create( 0, MOTOR_AXIS_NUM_PARAMS )) != NULL &&
|
||||
(pAxis->axisMutex = epicsMutexCreate( )) != NULL )
|
||||
{
|
||||
pAxis->card = card;
|
||||
pAxis->axis = axis;
|
||||
pAxis->hiHardLimit = hiLimit;
|
||||
pAxis->lowHardLimit = lowLimit;
|
||||
pAxis->home = home;
|
||||
pAxis->print = motorSimLogMsg;
|
||||
pAxis->logParam = NULL;
|
||||
motorParam->setDouble(pAxis->params, motorAxisPosition, start);
|
||||
*ppLast = pAxis;
|
||||
pAxis->print( pAxis->logParam, TRACE_FLOW, "Created motor for card %d, signal %d OK", card, axis );
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pAxis->route != NULL) routeDelete( pAxis->route );
|
||||
if (pAxis->params != NULL) motorParam->destroy( pAxis->params );
|
||||
if (pAxis->axisMutex != NULL) epicsMutexDestroy( pAxis->axisMutex );
|
||||
free ( pAxis );
|
||||
pAxis = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
free ( pAxis );
|
||||
pAxis = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pAxis->print( pAxis->logParam, TRACE_ERROR, "Motor for card %d, signal %d already exists", card, axis );
|
||||
return MOTOR_AXIS_ERROR;
|
||||
}
|
||||
|
||||
if (pAxis == NULL)
|
||||
{
|
||||
pAxis->print( pAxis->logParam, TRACE_ERROR, "Cannot create motor for card %d, signal %d", card, axis );
|
||||
return MOTOR_AXIS_ERROR;
|
||||
}
|
||||
|
||||
return MOTOR_AXIS_OK;
|
||||
}
|
||||
|
||||
|
||||
void motorSimCreate( int card, int axis, int lowLimit, int hiLimit, int home, int nCards, int nAxes, int startPosn )
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
|
||||
if (nCards < 1) nCards = 1;
|
||||
if (nAxes < 1 ) nAxes = 1;
|
||||
|
||||
drv.nAxes = nAxes;
|
||||
|
||||
drv.print( drv.logParam, TRACE_FLOW,
|
||||
"Creating motor simulator: card: %d, axis: %d, hi: %d, low %d, home: %d, ncards: %d, naxis: %d",
|
||||
card, axis, hiLimit, lowLimit, home, nCards, nAxes );
|
||||
|
||||
if (drv.motorThread==NULL)
|
||||
{
|
||||
drv.motorThread = epicsThreadCreate( "motorSimThread",
|
||||
epicsThreadPriorityLow,
|
||||
epicsThreadGetStackSize(epicsThreadStackMedium),
|
||||
(EPICSTHREADFUNC) motorSimTask, (void *) &drv );
|
||||
|
||||
if (drv.motorThread == NULL)
|
||||
{
|
||||
drv.print( drv.logParam, TRACE_ERROR, "Cannot start motor simulation thread" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for ( i = card; i < card+nCards; i++ )
|
||||
{
|
||||
for (j = axis; j < axis+nAxes; j++ )
|
||||
{
|
||||
motorSimCreateAxis( &drv, i, j, (double) lowLimit, (double) hiLimit, (double) home, (double) startPosn );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int motorSimLogMsg( void * param, const motorAxisLogMask_t mask, const char *pFormat, ...)
|
||||
{
|
||||
|
||||
va_list pvar;
|
||||
int nchar;
|
||||
|
||||
va_start(pvar, pFormat);
|
||||
nchar = vfprintf(stdout,pFormat,pvar);
|
||||
va_end (pvar);
|
||||
fprintf(stdout,"\n");
|
||||
return(nchar);
|
||||
}
|
13
motorSimApp/src/drvMotorSim.h
Normal file
13
motorSimApp/src/drvMotorSim.h
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef DRV_MOTOR_SIM_H
|
||||
#define DRV_MOTOR_SIM_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void motorSimCreate( int card, int axis, int hiLimit, int lowLimit, int home, int nCards, int nAxes, int startPosn );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
558
motorSimApp/src/motorSimDriver.cpp
Normal file
558
motorSimApp/src/motorSimDriver.cpp
Normal file
@ -0,0 +1,558 @@
|
||||
/*
|
||||
FILENAME... motorSimController.cpp
|
||||
USAGE... Simulated Motor Support.
|
||||
|
||||
Based on drvMotorSim.c
|
||||
|
||||
Mark Rivers
|
||||
December 13, 2009
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <epicsTime.h>
|
||||
#include <epicsThread.h>
|
||||
#include <epicsString.h>
|
||||
#include <epicsMutex.h>
|
||||
#include <ellLib.h>
|
||||
#include <iocsh.h>
|
||||
|
||||
#include "asynMotorController.h"
|
||||
#include "asynMotorAxis.h"
|
||||
|
||||
#include <epicsExport.h>
|
||||
#include "motorSimDriver.h"
|
||||
|
||||
#define DEFAULT_LOW_LIMIT -10000
|
||||
#define DEFAULT_HI_LIMIT 10000
|
||||
#define DEFAULT_HOME 0
|
||||
#define DEFAULT_START 0
|
||||
|
||||
static const char *driverName = "motorSimDriver";
|
||||
|
||||
static void motorSimTaskC(void *drvPvt);
|
||||
|
||||
typedef struct motorSimControllerNode {
|
||||
ELLNODE node;
|
||||
const char *portName;
|
||||
motorSimController *pController;
|
||||
} motorSimControllerNode;
|
||||
|
||||
static ELLLIST motorSimControllerList;
|
||||
static int motorSimControllerListInitialized = 0;
|
||||
|
||||
motorSimAxis::motorSimAxis(motorSimController *pController, int axis, double lowHardLimit, double hiHardLimit, double home, double start )
|
||||
: asynMotorAxis(pController, axis),
|
||||
pC_(pController),
|
||||
lowHardLimit_(lowHardLimit), hiHardLimit_(hiHardLimit), home_(home)
|
||||
{
|
||||
route_pars_t pars;
|
||||
|
||||
pars.numRoutedAxes = 1;
|
||||
pars.routedAxisList[0] = 1;
|
||||
pars.Tsync = 0.0;
|
||||
pars.Tcoast = 0.0;
|
||||
pars.axis[0].Amax = 1.0;
|
||||
pars.axis[0].Vmax = 1.0;
|
||||
homed_ = 0;
|
||||
|
||||
endpoint_.T = 0;
|
||||
endpoint_.axis[0].p = start;
|
||||
endpoint_.axis[0].v = 0;
|
||||
nextpoint_.T = 0;
|
||||
nextpoint_.axis[0].p = start;
|
||||
route_ = routeNew( &(this->endpoint_), &pars );
|
||||
deferred_move_ = 0;
|
||||
}
|
||||
|
||||
|
||||
motorSimController::motorSimController(const char *portName, int numAxes, int priority, int stackSize)
|
||||
: asynMotorController(portName, numAxes, NUM_SIM_CONTROLLER_PARAMS,
|
||||
asynInt32Mask | asynFloat64Mask,
|
||||
asynInt32Mask | asynFloat64Mask,
|
||||
ASYN_CANBLOCK | ASYN_MULTIDEVICE,
|
||||
1, // autoconnect
|
||||
priority, stackSize)
|
||||
{
|
||||
int axis;
|
||||
motorSimControllerNode *pNode;
|
||||
|
||||
if (!motorSimControllerListInitialized) {
|
||||
motorSimControllerListInitialized = 1;
|
||||
ellInit(&motorSimControllerList);
|
||||
}
|
||||
|
||||
// We should make sure this portName is not already in the list */
|
||||
pNode = (motorSimControllerNode*) calloc(1, sizeof(motorSimControllerNode));
|
||||
pNode->portName = epicsStrDup(portName);
|
||||
pNode->pController = this;
|
||||
ellAdd(&motorSimControllerList, (ELLNODE *)pNode);
|
||||
|
||||
if (numAxes < 1 ) numAxes = 1;
|
||||
numAxes_ = numAxes;
|
||||
this->movesDeferred_ = 0;
|
||||
for (axis=0; axis<numAxes; axis++) {
|
||||
new motorSimAxis(this, axis, DEFAULT_LOW_LIMIT, DEFAULT_HI_LIMIT, DEFAULT_HOME, DEFAULT_START);
|
||||
setDoubleParam(axis, this->motorPosition_, DEFAULT_START);
|
||||
}
|
||||
|
||||
this->motorThread_ = epicsThreadCreate("motorSimThread",
|
||||
epicsThreadPriorityLow,
|
||||
epicsThreadGetStackSize(epicsThreadStackMedium),
|
||||
(EPICSTHREADFUNC) motorSimTaskC, (void *) this);
|
||||
}
|
||||
|
||||
void motorSimController::report(FILE *fp, int level)
|
||||
{
|
||||
int axis;
|
||||
motorSimAxis *pAxis;
|
||||
|
||||
fprintf(fp, "Simulation motor driver %s, numAxes=%d\n",
|
||||
this->portName, numAxes_);
|
||||
|
||||
for (axis=0; axis<numAxes_; axis++) {
|
||||
pAxis = getAxis(axis);
|
||||
fprintf(fp, " axis %d\n", pAxis->axisNo_);
|
||||
|
||||
if (level > 0)
|
||||
{
|
||||
double lowSoftLimit=0.0;
|
||||
double hiSoftLimit=0.0;
|
||||
|
||||
fprintf(fp, " Current position = %f, velocity = %f at current time: %f\n",
|
||||
pAxis->nextpoint_.axis[0].p,
|
||||
pAxis->nextpoint_.axis[0].v,
|
||||
pAxis->nextpoint_.T);
|
||||
fprintf(fp, " Destination posn = %f, velocity = %f at desination time: %f\n",
|
||||
pAxis->endpoint_.axis[0].p,
|
||||
pAxis->endpoint_.axis[0].v,
|
||||
pAxis->endpoint_.T);
|
||||
|
||||
fprintf(fp, " Hard limits: %f, %f\n", pAxis->lowHardLimit_, pAxis->hiHardLimit_);
|
||||
fprintf(fp, " Home: %f\n", pAxis->home_);
|
||||
fprintf(fp, " Enc. offset: %f\n", pAxis->enc_offset_);
|
||||
getDoubleParam(pAxis->axisNo_, motorHighLimit_, &hiSoftLimit);
|
||||
getDoubleParam(pAxis->axisNo_, motorLowLimit_, &lowSoftLimit);
|
||||
fprintf(fp, " Soft limits: %f, %f\n", lowSoftLimit, hiSoftLimit );
|
||||
|
||||
if (pAxis->homing_) fprintf(fp, " Currently homing axis\n" );
|
||||
}
|
||||
}
|
||||
|
||||
// Call the base class method
|
||||
asynMotorController::report(fp, level);
|
||||
}
|
||||
|
||||
asynStatus motorSimController::processDeferredMoves()
|
||||
{
|
||||
asynStatus status = asynError;
|
||||
double position = 0.0;
|
||||
int axis;
|
||||
motorSimAxis *pAxis;
|
||||
|
||||
for (axis=0; axis<numAxes_; axis++)
|
||||
{
|
||||
pAxis = getAxis(axis);
|
||||
if (pAxis->deferred_move_) {
|
||||
position = pAxis->deferred_position_;
|
||||
/* Check to see if in hard limits */
|
||||
if ((pAxis->nextpoint_.axis[0].p >= pAxis->hiHardLimit_ && position > pAxis->nextpoint_.axis[0].p) ||
|
||||
(pAxis->nextpoint_.axis[0].p <= pAxis->lowHardLimit_ && position < pAxis->nextpoint_.axis[0].p)) return asynError;
|
||||
pAxis->endpoint_.axis[0].p = position - pAxis->enc_offset_;
|
||||
pAxis->endpoint_.axis[0].v = 0.0;
|
||||
setIntegerParam(axis, motorStatusDone_, 0);
|
||||
pAxis->deferred_move_ = 0;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus motorSimController::writeInt32(asynUser *pasynUser, epicsInt32 value)
|
||||
{
|
||||
int function = pasynUser->reason;
|
||||
asynStatus status = asynSuccess;
|
||||
motorSimAxis *pAxis = this->getAxis(pasynUser);
|
||||
static const char *functionName = "writeInt32";
|
||||
|
||||
|
||||
/* 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 == motorDeferMoves_)
|
||||
{
|
||||
asynPrint(pasynUser, ASYN_TRACE_FLOW,
|
||||
"%s:%s: %sing Deferred Move flag on driver %s\n",
|
||||
value != 0.0?"Sett":"Clear",
|
||||
driverName, functionName, this->portName);
|
||||
if (value == 0.0 && movesDeferred_ != 0)
|
||||
{
|
||||
processDeferredMoves();
|
||||
}
|
||||
movesDeferred_ = value;
|
||||
} else {
|
||||
/* Call base class call its method (if we have our parameters check this here) */
|
||||
status = asynMotorController::writeInt32(pasynUser, value);
|
||||
}
|
||||
|
||||
/* Do callbacks so higher layers see any changes */
|
||||
pAxis->callParamCallbacks();
|
||||
if (status)
|
||||
asynPrint(pasynUser, ASYN_TRACE_ERROR,
|
||||
"%s:%s: error, status=%d function=%d, value=%d\n",
|
||||
driverName, functionName, status, function, value);
|
||||
else
|
||||
asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
|
||||
"%s:%s: function=%d, value=%d\n",
|
||||
driverName, functionName, function, value);
|
||||
return status;
|
||||
}
|
||||
|
||||
motorSimAxis* motorSimController::getAxis(asynUser *pasynUser)
|
||||
{
|
||||
return static_cast<motorSimAxis*>(asynMotorController::getAxis(pasynUser));
|
||||
}
|
||||
|
||||
motorSimAxis* motorSimController::getAxis(int axisNo)
|
||||
{
|
||||
return static_cast<motorSimAxis*>(asynMotorController::getAxis(axisNo));
|
||||
}
|
||||
|
||||
asynStatus motorSimController::profileMove(asynUser *pasynUser, int npoints, double positions[], double times[], int relative, int trigger )
|
||||
{
|
||||
return asynError;
|
||||
}
|
||||
|
||||
asynStatus motorSimController::triggerProfile(asynUser *pasynUser)
|
||||
{
|
||||
return asynError;
|
||||
}
|
||||
|
||||
static void motorSimTaskC(void *drvPvt)
|
||||
{
|
||||
motorSimController *pController = (motorSimController*)drvPvt;
|
||||
pController->motorSimTask();
|
||||
}
|
||||
|
||||
|
||||
#define DELTA 0.1
|
||||
void motorSimController::motorSimTask()
|
||||
{
|
||||
epicsTimeStamp now;
|
||||
double delta;
|
||||
int axis;
|
||||
motorSimAxis *pAxis;
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
/* Get a new timestamp */
|
||||
epicsTimeGetCurrent( &now );
|
||||
delta = epicsTimeDiffInSeconds( &now, &(prevTime_) );
|
||||
prevTime_ = now;
|
||||
|
||||
if ( delta > (DELTA/4.0) && delta <= (4.0*DELTA) )
|
||||
{
|
||||
/* A reasonable time has elapsed, it's not a time step in the clock */
|
||||
for (axis=0; axis<numAxes_; axis++)
|
||||
{
|
||||
this->lock();
|
||||
pAxis = getAxis(axis);
|
||||
pAxis->process(delta );
|
||||
this->unlock();
|
||||
}
|
||||
}
|
||||
epicsThreadSleep( DELTA );
|
||||
}
|
||||
}
|
||||
|
||||
asynStatus motorSimAxis::move(double position, int relative, double minVelocity, double maxVelocity, double acceleration)
|
||||
{
|
||||
route_pars_t pars;
|
||||
static const char *functionName = "move";
|
||||
|
||||
if (relative) position += endpoint_.axis[0].p + enc_offset_;
|
||||
|
||||
/* Check to see if in hard limits */
|
||||
if ((nextpoint_.axis[0].p >= hiHardLimit_ && position > nextpoint_.axis[0].p) ||
|
||||
(nextpoint_.axis[0].p <= lowHardLimit_ && position < nextpoint_.axis[0].p) ) return asynError;
|
||||
|
||||
if (pC_->movesDeferred_ == 0) { /*Normal move.*/
|
||||
endpoint_.axis[0].p = position - enc_offset_;
|
||||
endpoint_.axis[0].v = 0.0;
|
||||
} else { /*Deferred moves.*/
|
||||
deferred_position_ = position;
|
||||
deferred_move_ = 1;
|
||||
deferred_relative_ = relative;
|
||||
}
|
||||
routeGetParams(route_, &pars);
|
||||
if (maxVelocity != 0) pars.axis[0].Vmax = fabs(maxVelocity);
|
||||
if (acceleration != 0) pars.axis[0].Amax = fabs(acceleration);
|
||||
routeSetParams( route_, &pars );
|
||||
|
||||
setIntegerParam(pC_->motorStatusDone_, 0);
|
||||
callParamCallbacks();
|
||||
|
||||
asynPrint(pasynUser_, ASYN_TRACE_FLOW,
|
||||
"%s:%s: Set driver %s, axis %d move to %f, min vel=%f, maxVel=%f, accel=%f\n",
|
||||
driverName, functionName, pC_->portName, axisNo_, position, minVelocity, maxVelocity, acceleration );
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
asynStatus motorSimAxis::setVelocity(double velocity, double acceleration )
|
||||
{
|
||||
route_pars_t pars;
|
||||
double deltaV = velocity - this->nextpoint_.axis[0].v;
|
||||
double time;
|
||||
|
||||
/* Check to see if in hard limits */
|
||||
if ((this->nextpoint_.axis[0].p > hiHardLimit_ && velocity > 0) ||
|
||||
(this->nextpoint_.axis[0].p < lowHardLimit_ && velocity < 0) ) return asynError;
|
||||
|
||||
routeGetParams( this->route_, &pars );
|
||||
if (acceleration != 0) pars.axis[0].Amax = fabs(acceleration);
|
||||
routeSetParams( this->route_, &pars );
|
||||
|
||||
time = fabs( deltaV / pars.axis[0].Amax );
|
||||
|
||||
this->endpoint_.axis[0].v = velocity;
|
||||
this->endpoint_.axis[0].p = (this->nextpoint_.axis[0].p +
|
||||
time * ( this->nextpoint_.axis[0].v + 0.5 * deltaV));
|
||||
this->reroute_ = ROUTE_NEW_ROUTE;
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
|
||||
asynStatus motorSimAxis::home(double minVelocity, double maxVelocity, double acceleration, int forwards )
|
||||
{
|
||||
asynStatus status = asynError;
|
||||
// static const char *functionName = "home";
|
||||
|
||||
status = setVelocity((forwards? maxVelocity: -maxVelocity), acceleration );
|
||||
homing_ = 1;
|
||||
homed_ = 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
asynStatus motorSimAxis::moveVelocity(double minVelocity, double velocity, double acceleration )
|
||||
{
|
||||
asynStatus status = asynError;
|
||||
// static const char *functionName = "moveVelocity";
|
||||
|
||||
status = setVelocity(velocity, acceleration );
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus motorSimAxis::stop(double acceleration )
|
||||
{
|
||||
// static const char *functionName = "moveVelocityAxis";
|
||||
|
||||
setVelocity(0.0, acceleration );
|
||||
deferred_move_ = 0;
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
asynStatus motorSimAxis::setPosition(double position)
|
||||
{
|
||||
enc_offset_ = position - nextpoint_.axis[0].p;
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
asynStatus motorSimAxis::config(int hiHardLimit, int lowHardLimit, int home, int start)
|
||||
{
|
||||
hiHardLimit_ = hiHardLimit;
|
||||
lowHardLimit_ = lowHardLimit;
|
||||
home_ = home;
|
||||
enc_offset_ = start;
|
||||
homed_ = 0;
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
asynStatus motorSimAxis::poll(bool *moving)
|
||||
{
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
|
||||
/** Process one iteration of an axis
|
||||
|
||||
This routine takes a single axis and propogates its motion forward a given amount
|
||||
of time.
|
||||
|
||||
\param delta [in] Time in seconds to propogate motion forwards.
|
||||
|
||||
\return Integer indicating 0 (asynSuccess) for success or non-zero for failure.
|
||||
*/
|
||||
|
||||
void motorSimAxis::process(double delta )
|
||||
{
|
||||
double lastpos;
|
||||
int done = 0;
|
||||
double postMoveDelay = 0.0;
|
||||
epicsTimeStamp nowTime;
|
||||
double nowTimeSecs = 0.0;
|
||||
|
||||
lastpos = nextpoint_.axis[0].p;
|
||||
nextpoint_.T += delta;
|
||||
routeFind( route_, reroute_, &endpoint_, &nextpoint_ );
|
||||
/* if (reroute_ == ROUTE_NEW_ROUTE) routePrint( route_, reroute_, &endpoint_, &nextpoint_, stdout ); */
|
||||
reroute_ = ROUTE_CALC_ROUTE;
|
||||
|
||||
/* No, do a limits check */
|
||||
if (homing_ &&
|
||||
((lastpos - home_) * (nextpoint_.axis[0].p - home_)) <= 0)
|
||||
{
|
||||
/* Homing and have crossed the home sensor - return to home */
|
||||
homing_ = 0;
|
||||
homed_ = 1;
|
||||
reroute_ = ROUTE_NEW_ROUTE;
|
||||
endpoint_.axis[0].p = home_;
|
||||
endpoint_.axis[0].v = 0.0;
|
||||
}
|
||||
if ( nextpoint_.axis[0].p > hiHardLimit_ && nextpoint_.axis[0].v > 0 )
|
||||
{
|
||||
if (homing_) setVelocity(-endpoint_.axis[0].v, 0.0 );
|
||||
else
|
||||
{
|
||||
reroute_ = ROUTE_NEW_ROUTE;
|
||||
endpoint_.axis[0].p = hiHardLimit_;
|
||||
endpoint_.axis[0].v = 0.0;
|
||||
}
|
||||
}
|
||||
else if (nextpoint_.axis[0].p < lowHardLimit_ && nextpoint_.axis[0].v < 0)
|
||||
{
|
||||
if (homing_) setVelocity(-endpoint_.axis[0].v, 0.0 );
|
||||
else
|
||||
{
|
||||
reroute_ = ROUTE_NEW_ROUTE;
|
||||
endpoint_.axis[0].p = lowHardLimit_;
|
||||
endpoint_.axis[0].v = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
if (nextpoint_.axis[0].v == 0) {
|
||||
if (!deferred_move_) {
|
||||
if (!delayedDone_) {
|
||||
done = 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
done = 0;
|
||||
}
|
||||
|
||||
//Post move delay
|
||||
epicsTimeGetCurrent(&nowTime);
|
||||
pC_->getDoubleParam(axisNo_, pC_->motorPostMoveDelay_, &postMoveDelay);
|
||||
if ((lastDone_ == 0) && (done == 1)) {
|
||||
if (postMoveDelay > 0) {
|
||||
delayedDone_ = 1;
|
||||
done = 0;
|
||||
lastTimeSecs_ = nowTime.secPastEpoch + (nowTime.nsec / 1.e9);
|
||||
}
|
||||
}
|
||||
if (delayedDone_ == 1) {
|
||||
nowTimeSecs = nowTime.secPastEpoch + (nowTime.nsec / 1.e9);
|
||||
if ((nowTimeSecs - lastTimeSecs_) >= postMoveDelay) {
|
||||
done = 1;
|
||||
delayedDone_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
lastDone_ = done;
|
||||
|
||||
setDoubleParam (pC_->motorPosition_, (nextpoint_.axis[0].p+enc_offset_));
|
||||
setDoubleParam (pC_->motorEncoderPosition_, (nextpoint_.axis[0].p+enc_offset_));
|
||||
setIntegerParam(pC_->motorStatusDirection_, (nextpoint_.axis[0].v > 0));
|
||||
setIntegerParam(pC_->motorStatusDone_, done);
|
||||
setIntegerParam(pC_->motorStatusHighLimit_, (nextpoint_.axis[0].p >= hiHardLimit_));
|
||||
setIntegerParam(pC_->motorStatusHome_, (nextpoint_.axis[0].p == home_));
|
||||
setIntegerParam(pC_->motorStatusHomed_, (homed_ == 1));
|
||||
setIntegerParam(pC_->motorStatusMoving_, !done);
|
||||
setIntegerParam(pC_->motorStatusLowLimit_, (nextpoint_.axis[0].p <= lowHardLimit_));
|
||||
callParamCallbacks();
|
||||
}
|
||||
|
||||
/** Configuration command, called directly or from iocsh */
|
||||
extern "C" int motorSimCreateController(const char *portName, int numAxes, int priority, int stackSize)
|
||||
{
|
||||
new motorSimController(portName,numAxes, priority, stackSize);
|
||||
return(asynSuccess);
|
||||
}
|
||||
|
||||
extern "C" int motorSimConfigAxis(const char *portName, int axis, int hiHardLimit, int lowHardLimit, int home, int start)
|
||||
{
|
||||
motorSimControllerNode *pNode;
|
||||
static const char *functionName = "motorSimConfigAxis";
|
||||
|
||||
// Find this controller
|
||||
if (!motorSimControllerListInitialized) {
|
||||
printf("%s:%s: ERROR, controller list not initialized\n",
|
||||
driverName, functionName);
|
||||
return(-1);
|
||||
}
|
||||
pNode = (motorSimControllerNode*)ellFirst(&motorSimControllerList);
|
||||
while(pNode) {
|
||||
if (strcmp(pNode->portName, portName) == 0) {
|
||||
printf("%s:%s: configuring controller %s axis %d\n",
|
||||
driverName, functionName, pNode->portName, axis);
|
||||
pNode->pController->getAxis(axis)->config(hiHardLimit, lowHardLimit, home, start);
|
||||
return(0);
|
||||
}
|
||||
pNode = (motorSimControllerNode*)ellNext((ELLNODE*)pNode);
|
||||
}
|
||||
printf("Controller not found\n");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
/** Code for iocsh registration */
|
||||
static const iocshArg motorSimCreateControllerArg0 = {"Port name", iocshArgString};
|
||||
static const iocshArg motorSimCreateControllerArg1 = {"Number of axes", iocshArgInt};
|
||||
static const iocshArg motorSimCreateControllerArg2 = {"priority", iocshArgInt};
|
||||
static const iocshArg motorSimCreateControllerArg3 = {"stackSize", iocshArgInt};
|
||||
static const iocshArg * const motorSimCreateControllerArgs[] = {&motorSimCreateControllerArg0,
|
||||
&motorSimCreateControllerArg1,
|
||||
&motorSimCreateControllerArg2,
|
||||
&motorSimCreateControllerArg3};
|
||||
static const iocshFuncDef motorSimCreateControllerDef = {"motorSimCreateController", 4, motorSimCreateControllerArgs};
|
||||
static void motorSimCreateContollerCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
motorSimCreateController(args[0].sval, args[1].ival, args[2].ival, args[3].ival);
|
||||
}
|
||||
|
||||
static const iocshArg motorSimConfigAxisArg0 = { "Post name", iocshArgString};
|
||||
static const iocshArg motorSimConfigAxisArg1 = { "Axis #", iocshArgInt};
|
||||
static const iocshArg motorSimConfigAxisArg2 = { "High limit", iocshArgInt};
|
||||
static const iocshArg motorSimConfigAxisArg3 = { "Low limit", iocshArgInt};
|
||||
static const iocshArg motorSimConfigAxisArg4 = { "Home position", iocshArgInt};
|
||||
static const iocshArg motorSimConfigAxisArg5 = { "Start posn", iocshArgInt};
|
||||
|
||||
static const iocshArg *const motorSimConfigAxisArgs[] = {
|
||||
&motorSimConfigAxisArg0,
|
||||
&motorSimConfigAxisArg1,
|
||||
&motorSimConfigAxisArg2,
|
||||
&motorSimConfigAxisArg3,
|
||||
&motorSimConfigAxisArg4,
|
||||
&motorSimConfigAxisArg5
|
||||
};
|
||||
static const iocshFuncDef motorSimConfigAxisDef ={"motorSimConfigAxis",6,motorSimConfigAxisArgs};
|
||||
|
||||
static void motorSimConfigAxisCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
motorSimConfigAxis(args[0].sval, args[1].ival, args[2].ival, args[3].ival, args[4].ival, args[5].ival);
|
||||
}
|
||||
|
||||
static void motorSimDriverRegister(void)
|
||||
{
|
||||
|
||||
iocshRegister(&motorSimCreateControllerDef, motorSimCreateContollerCallFunc);
|
||||
iocshRegister(&motorSimConfigAxisDef, motorSimConfigAxisCallFunc);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
epicsExportRegistrar(motorSimDriverRegister);
|
||||
}
|
85
motorSimApp/src/motorSimDriver.h
Normal file
85
motorSimApp/src/motorSimDriver.h
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
FILENAME... motorSimController.h
|
||||
USAGE... Simulated Motor Support.
|
||||
|
||||
Based on drvMotorSim.c
|
||||
|
||||
Mark Rivers
|
||||
March 28, 2010
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include <epicsTime.h>
|
||||
#include <epicsThread.h>
|
||||
|
||||
#include "asynMotorController.h"
|
||||
#include "asynMotorAxis.h"
|
||||
#include "route.h"
|
||||
|
||||
#define NUM_SIM_CONTROLLER_PARAMS 0
|
||||
|
||||
class epicsShareClass motorSimAxis : public asynMotorAxis
|
||||
{
|
||||
public:
|
||||
|
||||
/* These are the pure virtual functions that must be implemented */
|
||||
motorSimAxis(class motorSimController *pController, int axis, double lowLimit, double hiLimit, double home, double start);
|
||||
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);
|
||||
|
||||
/* These are the methods that are new to this class */
|
||||
asynStatus config(int hiHardLimit, int lowHardLimit, int home, int start);
|
||||
asynStatus setVelocity(double velocity, double acceleration);
|
||||
void process(double delta );
|
||||
|
||||
private:
|
||||
motorSimController *pC_;
|
||||
ROUTE_ID route_;
|
||||
route_reroute_t reroute_;
|
||||
route_demand_t endpoint_;
|
||||
route_demand_t nextpoint_;
|
||||
double lowHardLimit_;
|
||||
double hiHardLimit_;
|
||||
double enc_offset_;
|
||||
double home_;
|
||||
int homed_;
|
||||
int homing_;
|
||||
epicsTimeStamp tLast_;
|
||||
double deferred_position_;
|
||||
int deferred_move_;
|
||||
int deferred_relative_;
|
||||
double lastTimeSecs_;
|
||||
int delayedDone_;
|
||||
int lastDone_;
|
||||
|
||||
friend class motorSimController;
|
||||
};
|
||||
|
||||
class epicsShareClass motorSimController : asynMotorController {
|
||||
public:
|
||||
|
||||
/* These are the fucntions we override from the base class */
|
||||
motorSimController(const char *portName, int numAxes, int priority, int stackSize);
|
||||
asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
|
||||
void report(FILE *fp, int level);
|
||||
motorSimAxis* getAxis(asynUser *pasynUser);
|
||||
motorSimAxis* getAxis(int axisNo);
|
||||
asynStatus profileMove(asynUser *pasynUser, int npoints, double positions[], double times[], int relative, int trigger);
|
||||
asynStatus triggerProfile(asynUser *pasynUser);
|
||||
|
||||
/* These are the functions that are new to this class */
|
||||
void motorSimTask(); // Should be pivate, but called from non-member function
|
||||
|
||||
private:
|
||||
asynStatus processDeferredMoves();
|
||||
epicsThreadId motorThread_;
|
||||
epicsTimeStamp prevTime_;
|
||||
int movesDeferred_;
|
||||
|
||||
friend class motorSimAxis;
|
||||
};
|
23
motorSimApp/src/motorSimMain.cpp
Normal file
23
motorSimApp/src/motorSimMain.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
/* motorMain.cpp */
|
||||
/* Author: Marty Kraimer Date: 17MAR2000 */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "epicsExit.h"
|
||||
#include "epicsThread.h"
|
||||
#include "iocsh.h"
|
||||
|
||||
int main(int argc,char *argv[])
|
||||
{
|
||||
if(argc>=2) {
|
||||
iocsh(argv[1]);
|
||||
epicsThreadSleep(.2);
|
||||
}
|
||||
iocsh(NULL);
|
||||
epicsExit(0);
|
||||
return(0);
|
||||
}
|
40
motorSimApp/src/motorSimRegister.cc
Normal file
40
motorSimApp/src/motorSimRegister.cc
Normal file
@ -0,0 +1,40 @@
|
||||
#include <iocsh.h>
|
||||
#include "drvMotorSim.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
static const iocshArg motorSimCreateArg0 = { "Card", iocshArgInt};
|
||||
static const iocshArg motorSimCreateArg1 = { "Signal", iocshArgInt};
|
||||
static const iocshArg motorSimCreateArg2 = { "High limit", iocshArgInt};
|
||||
static const iocshArg motorSimCreateArg3 = { "Low limit", iocshArgInt};
|
||||
static const iocshArg motorSimCreateArg4 = { "Home position", iocshArgInt};
|
||||
static const iocshArg motorSimCreateArg5 = { "Num cards", iocshArgInt};
|
||||
static const iocshArg motorSimCreateArg6 = { "Num signals", iocshArgInt};
|
||||
static const iocshArg motorSimCreateArg7 = { "Start posn", iocshArgInt};
|
||||
|
||||
static const iocshArg *const motorSimCreateArgs[] = {
|
||||
&motorSimCreateArg0,
|
||||
&motorSimCreateArg1,
|
||||
&motorSimCreateArg2,
|
||||
&motorSimCreateArg3,
|
||||
&motorSimCreateArg4,
|
||||
&motorSimCreateArg5,
|
||||
&motorSimCreateArg6,
|
||||
&motorSimCreateArg7,
|
||||
};
|
||||
static const iocshFuncDef motorSimCreateDef ={"motorSimCreate",8,motorSimCreateArgs};
|
||||
|
||||
static void motorSimCreateCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
motorSimCreate(args[0].ival, args[1].ival, args[2].ival, args[3].ival, args[4].ival, args[5].ival, args[6].ival, args[7].ival);
|
||||
}
|
||||
|
||||
void motorSimRegister(void)
|
||||
{
|
||||
iocshRegister(&motorSimCreateDef, motorSimCreateCallFunc);
|
||||
}
|
||||
epicsExportRegistrar(motorSimRegister);
|
||||
|
||||
} // extern "C"
|
||||
|
4
motorSimApp/src/motorSimSupport.dbd
Normal file
4
motorSimApp/src/motorSimSupport.dbd
Normal file
@ -0,0 +1,4 @@
|
||||
device(motor,VME_IO,devMotorSim,"Motor Simulation")
|
||||
driver(motorSim)
|
||||
registrar(motorSimRegister)
|
||||
registrar(motorSimDriverRegister)
|
60
motorSimApp/src/motorSimTest.db
Normal file
60
motorSimApp/src/motorSimTest.db
Normal file
@ -0,0 +1,60 @@
|
||||
record(motor,"$(DEVICE):test0")
|
||||
{
|
||||
field(DESC,"Motor Simulation test")
|
||||
field(DTYP,"Motor Simulation")
|
||||
field(VELO,"1")
|
||||
field(VBAS,"0")
|
||||
field(VMAX,"1")
|
||||
field(HVEL,"1")
|
||||
field(ACCL,"1")
|
||||
field(BDST,"0.01")
|
||||
field(BVEL,"0.1")
|
||||
field(BACC,"0.1")
|
||||
field(OUT,"#C0 S0 @motorSim")
|
||||
field(MRES,"0.001")
|
||||
field(PREC,"5")
|
||||
field(EGU,"mm")
|
||||
field(DHLM,"30")
|
||||
field(DLLM,"-30")
|
||||
}
|
||||
|
||||
record(motor,"$(DEVICE):test1")
|
||||
{
|
||||
field(DESC,"Motor Simulation test")
|
||||
field(DTYP,"Motor Simulation")
|
||||
field(VELO,"1")
|
||||
field(VBAS,"0")
|
||||
field(VMAX,"1")
|
||||
field(HVEL,"1")
|
||||
field(ACCL,"1")
|
||||
field(BDST,"0.01")
|
||||
field(BVEL,"0.1")
|
||||
field(BACC,"0.1")
|
||||
field(OUT,"#C0 S1 @motorSim")
|
||||
field(MRES,"0.001")
|
||||
field(PREC,"5")
|
||||
field(EGU,"mm")
|
||||
field(DHLM,"30")
|
||||
field(DLLM,"-30")
|
||||
}
|
||||
|
||||
record(motor,"$(DEVICE):test2")
|
||||
{
|
||||
field(DESC,"Motor Simulation test")
|
||||
field(DTYP,"Motor Simulation")
|
||||
field(VELO,"1")
|
||||
field(VBAS,"0")
|
||||
field(VMAX,"1")
|
||||
field(HVEL,"1")
|
||||
field(ACCL,"1")
|
||||
field(BDST,"0.01")
|
||||
field(BVEL,"0.1")
|
||||
field(BACC,"0.1")
|
||||
field(OUT,"#C0 S2 @motorSim")
|
||||
field(MRES,"0.001")
|
||||
field(PREC,"5")
|
||||
field(EGU,"mm")
|
||||
field(DHLM,"30")
|
||||
field(DLLM,"-30")
|
||||
}
|
||||
|
38
motorSimApp/src/motorSimTest.src
Normal file
38
motorSimApp/src/motorSimTest.src
Normal file
@ -0,0 +1,38 @@
|
||||
#!$(INSTALL)/bin/$(ARCH)/motorSim
|
||||
|
||||
## You may have to change test to something else
|
||||
## everywhere it appears in this file
|
||||
|
||||
cd "$(INSTALL)"
|
||||
|
||||
# Load binaries on architectures that need to do so.
|
||||
# VXWORKS_ONLY, LINUX_ONLY and RTEMS_ONLY are macros that resolve
|
||||
# to a comment symbol on architectures that are not the current
|
||||
# build architecture, so they can be used liberally to do architecture
|
||||
# specific things. Alternatively, you can include an architecture
|
||||
# specific file.
|
||||
$(VXWORKS_ONLY)ld < bin/$(ARCH)/motorSim.munch
|
||||
|
||||
## This drvTS initializer is needed if the IOC has a hardware event system
|
||||
#TSinit
|
||||
|
||||
## Register all support components
|
||||
dbLoadDatabase("dbd/motorSim.dbd")
|
||||
motorSim_registerRecordDeviceDriver(pdbbase)
|
||||
|
||||
## Load record instances
|
||||
dbLoadRecords("db/motorSimTest.db","DEVICE=motorSim")
|
||||
#dbLoadRecords("db/dbExample2.db","user=npr78,no=1,scan=1 second")
|
||||
#dbLoadRecords("db/dbExample2.db","user=npr78,no=2,scan=2 second")
|
||||
#dbLoadRecords("db/dbExample2.db","user=npr78,no=3,scan=5 second")
|
||||
#dbLoadRecords("db/dbSubExample.db","user=npr78")
|
||||
|
||||
## Set this to see messages from mySub
|
||||
#mySubDebug 1
|
||||
|
||||
motorSimCreate( 0, 0, -22000, 42000, 10000, 1, 3, 5000 )
|
||||
|
||||
iocInit()
|
||||
|
||||
## Start any sequence programs
|
||||
#seq sncExample,"user=npr78Host"
|
1320
motorSimApp/src/route.c
Normal file
1320
motorSimApp/src/route.c
Normal file
File diff suppressed because it is too large
Load Diff
69
motorSimApp/src/route.h
Normal file
69
motorSimApp/src/route.h
Normal file
@ -0,0 +1,69 @@
|
||||
#ifndef __INCrouteLibh
|
||||
#define __INCrouteLibh
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define NUM_AXES 3
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ROUTE_CALC_ROUTE = 0,
|
||||
ROUTE_NEW_ROUTE = 1,
|
||||
ROUTE_NO_NEW_ROUTE = 2
|
||||
} route_reroute_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ROUTE__OK = 0,
|
||||
ROUTE__BADROUTE = 1,
|
||||
ROUTE__BADPARAM = 2,
|
||||
ROUTE__NEGSQRT = 3,
|
||||
ROUTE__NEGTIME = 4
|
||||
} route_status_t;
|
||||
|
||||
typedef struct route_axis_demand_str
|
||||
{
|
||||
double p; /* Demand position for axis at a given time */
|
||||
double v; /* Demand velocity for axis at a given time */
|
||||
} route_axis_demand_t;
|
||||
|
||||
typedef struct route_demand_str
|
||||
{
|
||||
double T; /* Time at which demand is valid */
|
||||
route_axis_demand_t axis[NUM_AXES];
|
||||
} route_demand_t;
|
||||
|
||||
typedef struct route_axis_pars_str
|
||||
{
|
||||
double Amax; /* Maximum acceleration for this axis */
|
||||
double Vmax; /* Maximum velocity for this axis */
|
||||
} route_axis_pars_t;
|
||||
|
||||
typedef struct route_pars_str
|
||||
{
|
||||
unsigned int numRoutedAxes; /* Number of axes to be routed */
|
||||
int routedAxisList[NUM_AXES]; /* List of the axes to be routed */
|
||||
double Tsync; /* Synchronisation period for routing */
|
||||
double Tcoast; /* End of route coast time for all axes */
|
||||
route_axis_pars_t axis[NUM_AXES];
|
||||
} route_pars_t;
|
||||
|
||||
typedef struct route_str * ROUTE_ID;
|
||||
|
||||
ROUTE_ID routeNew( route_demand_t * initialDemand, route_pars_t * initial_parameters );
|
||||
route_status_t routeFind( ROUTE_ID, route_reroute_t, route_demand_t * end_demand, route_demand_t * next_demand );
|
||||
void routePrint( ROUTE_ID route, route_reroute_t reroute, route_demand_t * endp, route_demand_t * nextp, FILE * logfile );
|
||||
void routeDelete( ROUTE_ID );
|
||||
|
||||
route_status_t routeSetDemand( ROUTE_ID, route_demand_t * demand );
|
||||
route_status_t routeSetParams( ROUTE_ID, route_pars_t * parameters );
|
||||
route_status_t routeGetParams( ROUTE_ID, route_pars_t * parameters );
|
||||
route_status_t routeGetNumRoutedAxes( ROUTE_ID route, unsigned int * number );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __INCrouteLibh */
|
Reference in New Issue
Block a user