Initial commit of the PSI version of motorMotorSim as a standalone

driver.
This commit is contained in:
2025-02-14 15:12:33 +01:00
commit b240341bfd
55 changed files with 4155 additions and 0 deletions

5
.gitattributes vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,2 @@
#RULES.ioc
include $(CONFIG)/RULES.ioc

2
configure/RULES_DIRS Normal file
View File

@ -0,0 +1,2 @@
#RULES_DIRS
include $(CONFIG)/RULES_DIRS

3
configure/RULES_TOP Normal file
View File

@ -0,0 +1,3 @@
#RULES_TOP
include $(CONFIG)/RULES_TOP

33
docs/RELEASE.md Normal file
View 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
View 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
View 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

View 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

View 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

View File

@ -0,0 +1,8 @@
TOP=..
include $(TOP)/configure/CONFIG
TARGETS = $(CONFIG_TARGETS)
CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS)))
include $(TOP)/configure/RULES

View 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

View File

@ -0,0 +1,6 @@
# RULES
include $(CONFIG)/RULES
# Library should be rebuilt because LIBOBJS may have changed.
$(LIBNAME): ../Makefile

View File

@ -0,0 +1,2 @@
#RULES.ioc
include $(CONFIG)/RULES.ioc

View File

@ -0,0 +1,2 @@
#RULES_DIRS
include $(CONFIG)/RULES_DIRS

View File

@ -0,0 +1,3 @@
#RULES_TOP
include $(CONFIG)/RULES_TOP

View File

@ -0,0 +1,6 @@
TOP = ..
include $(TOP)/configure/CONFIG
DIRS += $(wildcard *ioc*)
DIRS += $(wildcard as*)
include $(CONFIG)/RULES_DIRS

View 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

View 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

View 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, ""}
}

View 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

View 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

View 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

View 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

View 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 &

View 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

View 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

View 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

View 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
View 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
View 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

View 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, ""}
}

View File

@ -0,0 +1,6 @@
TOP = ../..
include $(TOP)/configure/CONFIG
IOCSH += EXAMPLE_motorSim.substitutions
include $(TOP)/configure/RULES

View 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
View 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

View 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);
}

View 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);
}

View 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

View 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);
}

View 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;
};

View 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);
}

View 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"

View File

@ -0,0 +1,4 @@
device(motor,VME_IO,devMotorSim,"Motor Simulation")
driver(motorSim)
registrar(motorSimRegister)
registrar(motorSimDriverRegister)

View 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")
}

View 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

File diff suppressed because it is too large Load Diff

69
motorSimApp/src/route.h Normal file
View 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 */