Initial commit of SINQ EPICS Application
This commit is contained in:
17
Makefile
Normal file
17
Makefile
Normal file
@ -0,0 +1,17 @@
|
||||
#Makefile at top of application tree
|
||||
TOP = .
|
||||
include $(TOP)/configure/CONFIG
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), configure)
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *App))
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard iocBoot))
|
||||
|
||||
define DIR_template
|
||||
$(1)_DEPEND_DIRS = configure
|
||||
endef
|
||||
$(foreach dir, $(filter-out configure,$(DIRS)),$(eval $(call DIR_template,$(dir))))
|
||||
|
||||
iocBoot_DEPEND_DIRS += $(filter %App,$(DIRS))
|
||||
|
||||
include $(TOP)/configure/RULES_TOP
|
||||
|
||||
|
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
|
||||
|
33
configure/CONFIG_SITE
Normal file
33
configure/CONFIG_SITE
Normal file
@ -0,0 +1,33 @@
|
||||
# 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 anyway 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-68040
|
||||
|
||||
# To install files into a location other than $(TOP) define
|
||||
# INSTALL_LOCATION here.
|
||||
INSTALL_LOCATION=/usr/local/epics/sinqApp
|
||||
|
||||
# Set this when your IOC and the host use different paths
|
||||
# to access the application. This will be needed to boot
|
||||
# from a Microsoft FTP server or with some NFS mounts.
|
||||
# You must rebuild in the iocBoot directory for this to
|
||||
# take effect.
|
||||
#IOCS_APPL_TOP = </IOC/path/to/application/top>
|
8
configure/Makefile
Normal file
8
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
|
36
configure/RELEASE
Normal file
36
configure/RELEASE
Normal file
@ -0,0 +1,36 @@
|
||||
# RELEASE - Location of external support modules
|
||||
#
|
||||
# IF YOU MAKE ANY CHANGES to this file you must subsequently
|
||||
# do a "gnumake rebuild" in this application's top level
|
||||
# directory.
|
||||
#
|
||||
# The build process does not check dependencies against files
|
||||
# that are outside this application, thus you should do a
|
||||
# "gnumake rebuild" in the top level directory after EPICS_BASE
|
||||
# or any other external module pointed to below is rebuilt.
|
||||
#
|
||||
# Host- or target-specific settings can be given in files named
|
||||
# RELEASE.$(EPICS_HOST_ARCH).Common
|
||||
# RELEASE.Common.$(T_A)
|
||||
# RELEASE.$(EPICS_HOST_ARCH).$(T_A)
|
||||
#
|
||||
# This file should ONLY define paths to other support modules,
|
||||
# or include statements that pull in similar RELEASE files.
|
||||
# Build settings that are NOT module paths should appear in a
|
||||
# CONFIG_SITE file.
|
||||
|
||||
TEMPLATE_TOP=$(EPICS_BASE)/templates/makeBaseApp/top
|
||||
|
||||
# If using the sequencer, point SNCSEQ at its top directory:
|
||||
#SNCSEQ=$(EPICS_BASE)/../modules/soft/seq
|
||||
|
||||
# EPICS_BASE usually appears last so other apps can override stuff:
|
||||
EPICS_BASE=/usr/local/epics
|
||||
|
||||
# Set RULES here if you want to take build rules from somewhere
|
||||
# other than EPICS_BASE:
|
||||
#RULES=/path/to/epics/support/module/rules/x-y
|
||||
MOTOR=/usr/local/epics/support/motor-6-7
|
||||
ASYN=/usr/local/epics/support/asyn-4-18
|
||||
STD=/usr/local/epics/support/std-3-1
|
||||
ANC=/usr/local/epics/anc350v17
|
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
|
||||
|
6
iocBoot/Makefile
Normal file
6
iocBoot/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
TOP = ..
|
||||
include $(TOP)/configure/CONFIG
|
||||
DIRS += $(wildcard *ioc*)
|
||||
DIRS += $(wildcard as*)
|
||||
include $(CONFIG)/RULES_DIRS
|
||||
|
5
iocBoot/iocsinqEPICS/Makefile
Normal file
5
iocBoot/iocsinqEPICS/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
TOP = ../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
ARCH = linux-x86
|
||||
TARGETS = envPaths
|
||||
include $(TOP)/configure/RULES.ioc
|
10
iocBoot/iocsinqEPICS/motor.substitutions.el734
Normal file
10
iocBoot/iocsinqEPICS/motor.substitutions.el734
Normal file
@ -0,0 +1,10 @@
|
||||
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}
|
||||
{NZ:mota:, 1, "m$(N)", "asynMotor", mota, 1, "sgu", degree, Pos, 2.0, 0.1, .2, 0, 1, .2, 1., 3, 20, -20, ""}
|
||||
{NZ:mota:, 2, "m$(N)", "asynMotor", mota, 2, "sgl", degree, Pos, 2.0, 0.1, .2, 0, 1, .2, 1., 3, 20, -20, ""}
|
||||
{NZ:mota:, 3, "m$(N)", "asynMotor", mota, 3, "sgd", degree, Pos, 2.0, 0.1, .2, 0, 1, .2, 1., 3, 20, -20, ""}
|
||||
{NZ:mota:, 4, "m$(N)", "asynMotor", mota, 4, "som", degree, Pos, 2.0, 0.1, .2, 0, 1, .2, 0.001, 3, 37, -37, ""}
|
||||
{NZ:mota:, 5, "m$(N)", "asynMotor", mota, 5, "sty", degree, Pos, 2.0, 0.1, .2, 0, 1, .2, 0.001, 3, 150, -149, ""}
|
||||
}
|
9
iocBoot/iocsinqEPICS/motor.substitutions.mcb4b
Normal file
9
iocBoot/iocsinqEPICS/motor.substitutions.mcb4b
Normal file
@ -0,0 +1,9 @@
|
||||
file "$(TOP)/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", MCB4B1, 0, "Bottom", mm, Pos, 2.0, 0.1, .2, 0, 1, .2, -.001, 3, 16, 0, ""}
|
||||
{IOC:, 2, "m$(N)", "asynMotor", MCB4B1, 1, "Top", mm, Pos, 2.0, 0.1, .2, 0, 1, .2, -.001, 3, 16, 0, ""}
|
||||
{IOC:, 3, "m$(N)", "asynMotor", MCB4B1, 2, "Inboard", mm, Pos, 2.0, 0.1, .2, 0, 1, .2, -.001, 3, 16, 0, ""}
|
||||
{IOC:, 4, "m$(N)", "asynMotor", MCB4B1, 3, "Outboard", mm, Pos, 2.0, 0.1, .2, 0, 1, .2, -.001, 3, 16, 0, ""}
|
||||
}
|
13
iocBoot/iocsinqEPICS/motor.substitutions.orion
Normal file
13
iocBoot/iocsinqEPICS/motor.substitutions.orion
Normal file
@ -0,0 +1,13 @@
|
||||
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}
|
||||
{OR:mota:, 1, "m$(N)", "asynMotor", mota, 1, "mom1", degree, Pos, 2.0, 0.1, .2, 0, 1, .2, 0.001, 3, 19, -13.4, ""}
|
||||
{OR:mota:, 2, "m$(N)", "asynMotor", mota, 2, "mom2", degree, Pos, 2.0, 0.1, .2, 0, 1, .2, 0.001, 3, 63, -23.4, ""}
|
||||
{OR:mota:, 3, "m$(N)", "asynMotor", mota, 3, "som", degree, Pos, 2.0, 0.1, .2, 0, 1, .2, 0.001, 3, 42, -27, ""}
|
||||
{OR:mota:, 4, "m$(N)", "asynMotor", mota, 4, "stt", degree, Pos, 2.0, 0.1, .2, 0, 1, .2, 0.001, 3, 150.99, -47.99, ""}
|
||||
{OR:mota:, 5, "m$(N)", "asynMotor", mota, 5, "sgl", degree, Pos, 2.0, 0.1, .2, 0, 1, .2, 0.001, 3, 15.05, -15.14, ""}
|
||||
{OR:mota:, 6, "m$(N)", "asynMotor", mota, 6, "sgu", degree, Pos, 2.0, 0.1, .2, 0, 1, .2, 0.001, 3, 15.06, -15.41, ""}
|
||||
{OR:mota:, 7, "m$(N)", "asynMotor", mota, 7, "chi", degree, Pos, 2.0, 0.1, .2, 0, 1, .2, 0.001, 3, 131.05, -33.01, ""}
|
||||
{OR:mota:, 8, "m$(N)", "asynMotor", mota, 8, "phi", degree, Pos, 2.0, 0.1, .2, 0, 1, .2, 0.001, 3, 360.94, -1., ""}
|
||||
}
|
11873
iocBoot/iocsinqEPICS/orion.log
Normal file
11873
iocBoot/iocsinqEPICS/orion.log
Normal file
File diff suppressed because it is too large
Load Diff
43
iocBoot/iocsinqEPICS/st.cmd
Executable file
43
iocBoot/iocsinqEPICS/st.cmd
Executable file
@ -0,0 +1,43 @@
|
||||
#!../../bin/linux-x86/sinqEPICS
|
||||
|
||||
## You may have to change sinqEPICS to something else
|
||||
## everywhere it appears in this file
|
||||
|
||||
< envPaths
|
||||
|
||||
cd ${TOP}
|
||||
|
||||
## Register all support components
|
||||
dbLoadDatabase "dbd/sinqEPICS.dbd"
|
||||
dbLoadDatabase "dbd/sinq.dbd"
|
||||
sinqEPICS_registerRecordDeviceDriver pdbbase
|
||||
|
||||
## Load record instances
|
||||
#dbLoadRecords("db/xxx.db","user=koenneckeHost")
|
||||
|
||||
|
||||
#---------- load EL734 motor controller
|
||||
drvAsynIPPortConfigure("serial1", "narziss-ts:3002",0,0,0)
|
||||
#drvAsynIPPortConfigure("serial1", "localhost:8080",0,0,0)
|
||||
EL734CreateController("mota","serial1",6);
|
||||
|
||||
### Motors
|
||||
|
||||
dbLoadRecords("$(ASYN)/db/asynRecord.db","P=NZ:,R=serial1,PORT=serial1,ADDR=0,OMAX=80,IMAX=80")
|
||||
|
||||
|
||||
cd ${TOP}/iocBoot/${IOC}
|
||||
dbLoadTemplate "motor.substitutions.el734"
|
||||
|
||||
|
||||
#--------- load EL737 counter box
|
||||
drvAsynIPPortConfigure("cter1","narziss-ts:3003",0,0,0)
|
||||
dbLoadRecords("$(ASYN)/db/asynRecord.db","P=NZ:,R=cter1,PORT=cter1,ADDR=0,OMAX=80,IMAX=80")
|
||||
dbLoadRecords("${TOP}/db/el737Record.db")
|
||||
|
||||
asynSetTraceIOMask("cter1",0,2)
|
||||
|
||||
iocInit
|
||||
|
||||
## Start any sequence programs
|
||||
#seq sncxxx,"user=koenneckeHost"
|
31
iocBoot/iocsinqEPICS/st.cmd.mcb4b
Normal file
31
iocBoot/iocsinqEPICS/st.cmd.mcb4b
Normal file
@ -0,0 +1,31 @@
|
||||
#errlogInit(5000)
|
||||
< envPaths
|
||||
# Tell EPICS all about the record types, device-support modules, drivers,
|
||||
# etc. in this build from CARS
|
||||
dbLoadDatabase("../../dbd/WithAsyn.dbd")
|
||||
WithAsyn_registerRecordDeviceDriver(pdbbase)
|
||||
|
||||
drvAsynIPPortConfigure("serial1", "164.54.160.36:4002",0,0,0)
|
||||
asynOctetSetInputEos("serial1",0,"\r")
|
||||
asynOctetSetOutputEos("serial1",0,"\r")
|
||||
asynSetTraceIOMask("serial1", 0, 2)
|
||||
#asynSetTraceMask("serial1", 0, 255)
|
||||
|
||||
MCB4BCreateController("MCB4B1", "serial1", 4, 100, 5000)
|
||||
|
||||
### Motors
|
||||
dbLoadTemplate "motor.substitutions.mcb4b"
|
||||
|
||||
dbLoadRecords("$(ASYN)/db/asynRecord.db","P=IOC:,R=serial1,PORT=serial1,ADDR=0,OMAX=80,IMAX=80")
|
||||
|
||||
iocInit
|
||||
|
||||
# This IOC does not use save/restore, so set values of some PVs
|
||||
dbpf("IOC:m1.RTRY", "0")
|
||||
dbpf("IOC:m1.TWV", "0.1")
|
||||
dbpf("IOC:m2.RTRY", "0")
|
||||
dbpf("IOC:m2.TWV", "0.1")
|
||||
dbpf("IOC:m3.RTRY", "0")
|
||||
dbpf("IOC:m3.TWV", "0.1")
|
||||
dbpf("IOC:m4.RTRY", "0")
|
||||
dbpf("IOC:m4.TWV", "0.1")
|
34
iocBoot/iocsinqEPICS/std.cmd
Executable file
34
iocBoot/iocsinqEPICS/std.cmd
Executable file
@ -0,0 +1,34 @@
|
||||
###!../../bin/linux-x86/sinqEPICS
|
||||
# for debugging..................
|
||||
## You may have to change sinqEPICS to something else
|
||||
## everywhere it appears in this file
|
||||
|
||||
< envPaths
|
||||
|
||||
cd ${TOP}
|
||||
|
||||
## Register all support components
|
||||
dbLoadDatabase "dbd/sinqEPICS.dbd"
|
||||
dbLoadDatabase "dbd/sinq.dbd"
|
||||
sinqEPICS_registerRecordDeviceDriver pdbbase
|
||||
|
||||
## Load record instances
|
||||
#dbLoadRecords("db/xxx.db","user=koenneckeHost")
|
||||
|
||||
|
||||
#---------- load EL734 motor controller
|
||||
drvAsynIPPortConfigure("serial1", "narziss-ts:3002",0,0,0)
|
||||
EL734CreateController("mota","serial1",6);
|
||||
|
||||
### Motors
|
||||
|
||||
dbLoadRecords("$(ASYN)/db/asynRecord.db","P=IOC:,R=serial1,PORT=serial1,ADDR=0,OMAX=80,IMAX=80")
|
||||
|
||||
|
||||
cd ${TOP}/iocBoot/${IOC}
|
||||
dbLoadTemplate "motor.substitutions.el734"
|
||||
|
||||
iocInit
|
||||
|
||||
## Start any sequence programs
|
||||
#seq sncxxx,"user=koenneckeHost"
|
43
iocBoot/iocsinqEPICS/sto.cmd
Executable file
43
iocBoot/iocsinqEPICS/sto.cmd
Executable file
@ -0,0 +1,43 @@
|
||||
#!../../bin/linux-x86/sinqEPICS
|
||||
|
||||
## This is for ORION
|
||||
## You may have to change sinqEPICS to something else
|
||||
## everywhere it appears in this file
|
||||
|
||||
|
||||
< envPaths
|
||||
|
||||
cd ${TOP}
|
||||
|
||||
## Register all support components
|
||||
dbLoadDatabase "dbd/sinqEPICS.dbd"
|
||||
dbLoadDatabase "dbd/sinq.dbd"
|
||||
sinqEPICS_registerRecordDeviceDriver pdbbase
|
||||
|
||||
## Load record instances
|
||||
#dbLoadRecords("db/xxx.db","user=koenneckeHost")
|
||||
|
||||
#---------- load Delta Tau MCU
|
||||
#pmacAsynIPConfigure("mcu1","localhost:8080")
|
||||
pmacAsynIPConfigure("mcu1","orion-mcu1:1025")
|
||||
pmacCreateController("mota","mcu1",0,8,50,10000);
|
||||
pmacCreateAxes("mota",8);
|
||||
|
||||
dbLoadRecords("$(ASYN)/db/asynRecord.db","P=OR:,R=mcu1,PORT=mcu1,ADDR=0,OMAX=80,IMAX=80")
|
||||
|
||||
|
||||
cd ${TOP}/iocBoot/${IOC}
|
||||
dbLoadTemplate "motor.substitutions.orion"
|
||||
|
||||
|
||||
#--------- load EL737 counter box
|
||||
#drvAsynIPPortConfigure("cter1","orion-ts:3002",0,0,0)
|
||||
#dbLoadRecords("$(ASYN)/db/asynRecord.db","P=OZ:,R=cter1,PORT=cter1,ADDR=0,OMAX=80,IMAX=80")
|
||||
#dbLoadRecords("${TOP}/db/ORel737Record.db")
|
||||
|
||||
#asynSetTraceIOMask("cter1",0,2)
|
||||
|
||||
iocInit
|
||||
|
||||
## Start any sequence programs
|
||||
#seq sncxxx,"user=koenneckeHost"
|
22
sinqEPICSApp/Db/Makefile
Normal file
22
sinqEPICSApp/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
sinqEPICSApp/Makefile
Normal file
8
sinqEPICSApp/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
|
||||
|
430
sinqEPICSApp/src/EL734Driver.cpp
Normal file
430
sinqEPICSApp/src/EL734Driver.cpp
Normal file
@ -0,0 +1,430 @@
|
||||
/*
|
||||
FILENAME... EL734Driver.cpp
|
||||
USAGE... Motor driver support for the PSI EL734 controller.
|
||||
|
||||
Mark Koennecke
|
||||
February 2013
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <iocsh.h>
|
||||
#include <epicsThread.h>
|
||||
#include <errlog.h>
|
||||
|
||||
#include <asynOctetSyncIO.h>
|
||||
|
||||
#include "EL734Driver.h"
|
||||
#include <epicsExport.h>
|
||||
|
||||
#define IDLEPOLL 60
|
||||
|
||||
|
||||
/** Creates a new EL734Controller object.
|
||||
* \param[in] portName The name of the asyn port that will be created for this driver
|
||||
* \param[in] EL734PortName The name of the drvAsynSerialPort that was created previously to connect to the EL734 controller
|
||||
* \param[in] numAxes The number of axes that this controller supports
|
||||
*/
|
||||
EL734Controller::EL734Controller(const char *portName, const char *EL734PortName, int numAxes)
|
||||
: asynMotorController(portName, numAxes+1, 0,
|
||||
0, // No additional interfaces beyond those in base class
|
||||
0, // No additional callback interfaces beyond those in base class
|
||||
ASYN_CANBLOCK | ASYN_MULTIDEVICE,
|
||||
1, // autoconnect
|
||||
0, 0) // Default priority and stack size
|
||||
{
|
||||
int axis;
|
||||
asynStatus status;
|
||||
EL734Axis *pAxis;
|
||||
static const char *functionName = "EL734Controller::EL734Controller";
|
||||
|
||||
/* Connect to EL734 controller */
|
||||
status = pasynOctetSyncIO->connect(EL734PortName, 0, &pasynUserController_, NULL);
|
||||
if (status) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s: cannot connect to EL734 controller\n",
|
||||
functionName);
|
||||
}
|
||||
pasynOctetSyncIO->setOutputEos(pasynUserController_,"\r",strlen("\r"));
|
||||
pasynOctetSyncIO->setInputEos(pasynUserController_,"\r",strlen("\r"));
|
||||
|
||||
switchRemote();
|
||||
|
||||
for (axis=0; axis<numAxes; axis++) {
|
||||
pAxis = new EL734Axis(this, axis+1);
|
||||
}
|
||||
|
||||
startPoller(1000./1000., IDLEPOLL, 2);
|
||||
}
|
||||
|
||||
|
||||
/** Creates a new EL734Controller object.
|
||||
* Configuration command, called directly or from iocsh
|
||||
* \param[in] portName The name of the asyn port that will be created for this driver
|
||||
* \param[in] EL734PortName The name of the drvAsynIPPPort that was created previously to connect to the EL734 controller
|
||||
* \param[in] numAxes The number of axes that this controller supports
|
||||
*/
|
||||
extern "C" int EL734CreateController(const char *portName, const char *EL734PortName, int numAxes)
|
||||
{
|
||||
EL734Controller *pEL734Controller
|
||||
= new EL734Controller(portName, EL734PortName, numAxes);
|
||||
pEL734Controller = NULL;
|
||||
return(asynSuccess);
|
||||
}
|
||||
|
||||
/** Reports on status of the driver
|
||||
* \param[in] fp The file pointer on which report information will be written
|
||||
* \param[in] level The level of report detail desired
|
||||
*
|
||||
* If details > 0 then information is printed about each axis.
|
||||
* After printing controller-specific information it calls asynMotorController::report()
|
||||
*/
|
||||
void EL734Controller::report(FILE *fp, int level)
|
||||
{
|
||||
fprintf(fp, "EL734 motor driver %s, numAxes=%d\n",
|
||||
this->portName, numAxes_);
|
||||
|
||||
// Call the base class method
|
||||
asynMotorController::report(fp, level);
|
||||
}
|
||||
|
||||
/** Returns a pointer to an EL734Axis object.
|
||||
* Returns NULL if the axis number encoded in pasynUser is invalid.
|
||||
* \param[in] pasynUser asynUser structure that encodes the axis index number. */
|
||||
EL734Axis* EL734Controller::getAxis(asynUser *pasynUser)
|
||||
{
|
||||
return static_cast<EL734Axis*>(asynMotorController::getAxis(pasynUser));
|
||||
}
|
||||
|
||||
/** Returns a pointer to an EL734Axis object.
|
||||
* Returns NULL if the axis number encoded in pasynUser is invalid.
|
||||
* \param[in] axisNo Axis index number. */
|
||||
EL734Axis* EL734Controller::getAxis(int axisNo)
|
||||
{
|
||||
return static_cast<EL734Axis*>(asynMotorController::getAxis(axisNo));
|
||||
}
|
||||
|
||||
void EL734Controller::switchRemote()
|
||||
{
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
int status;
|
||||
size_t in, out;
|
||||
int reason;
|
||||
|
||||
strcpy(command,"RMT 1");
|
||||
status = pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
|
||||
reply,COMLEN, 1.,&out,&in,&reason);
|
||||
strcpy(command,"ECHO 0");
|
||||
status = pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
|
||||
reply,COMLEN, 1.,&out,&in,&reason);
|
||||
strcpy(command,"RMT 1");
|
||||
status = pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
|
||||
reply,COMLEN, 1.,&out,&in,&reason);
|
||||
strcpy(command,"ECHO 0");
|
||||
status = pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
|
||||
reply,COMLEN, 1.,&out,&in,&reason);
|
||||
}
|
||||
/**
|
||||
* send a command to the EL734 and read the reply. Do some error and controller
|
||||
* issue fixing on the way
|
||||
* \param[in] command The command to send
|
||||
* \param[out] reply The controllers reply
|
||||
*/
|
||||
|
||||
asynStatus EL734Controller::transactController(char command[COMLEN], char reply[COMLEN])
|
||||
{
|
||||
asynStatus status;
|
||||
size_t in, out, i;
|
||||
int reason;
|
||||
char myReply[COMLEN];
|
||||
|
||||
pasynOctetSyncIO->flush(pasynUserController_);
|
||||
|
||||
status = pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
|
||||
reply,COMLEN, 1.,&out,&in,&reason);
|
||||
if(status != asynSuccess){
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
check for the offline reply
|
||||
*/
|
||||
if(strstr(reply,"?LOC") != NULL){
|
||||
switchRemote();
|
||||
return pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
|
||||
reply,COMLEN, 1.,&out,&in,&reason);
|
||||
}
|
||||
|
||||
/*
|
||||
check for echos. This means that the thing is offline.
|
||||
*/
|
||||
if(strstr(reply,"p ") != NULL || strstr(reply,"u ") != NULL || strstr(reply,"msr ") != NULL){
|
||||
switchRemote();
|
||||
return pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
|
||||
reply,COMLEN, 1.,&out,&in,&reason);
|
||||
}
|
||||
/*
|
||||
check for EL734 errors
|
||||
*/
|
||||
strcpy(myReply, reply);
|
||||
for(i = 0; i < strlen(reply); i++){
|
||||
myReply[i] = (char)tolower((int)reply[i]);
|
||||
}
|
||||
if(strstr(myReply,"?cmd") != NULL){
|
||||
errlogSevPrintf(errlogMajor, "Bad command %s", command);
|
||||
return asynError;
|
||||
} else if(strstr(myReply,"?par") != NULL){
|
||||
errlogSevPrintf(errlogMajor, "Bad parameter in command %s", command);
|
||||
return asynError;
|
||||
} else if(strstr(myReply,"?rng") != NULL){
|
||||
errlogSevPrintf(errlogMajor, "Parameter out of range in command %s", command);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// These are the EL734Axis methods
|
||||
|
||||
/** Creates a new EL734Axis object.
|
||||
* \param[in] pC Pointer to the EL734Controller to which this axis belongs.
|
||||
* \param[in] axisNo Index number of this axis, range 0 to pC->numAxes_-1.
|
||||
*
|
||||
* Initializes register numbers, etc.
|
||||
*/
|
||||
EL734Axis::EL734Axis(EL734Controller *pC, int axisNo)
|
||||
: asynMotorAxis(pC, axisNo),
|
||||
pC_(pC)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Reports on status of the axis
|
||||
* \param[in] fp The file pointer on which report information will be written
|
||||
* \param[in] level The level of report detail desired
|
||||
*
|
||||
* After printing device-specific information calls asynMotorAxis::report()
|
||||
*/
|
||||
void EL734Axis::report(FILE *fp, int level)
|
||||
{
|
||||
if (level > 0) {
|
||||
fprintf(fp, " axis %d\n",
|
||||
axisNo_);
|
||||
}
|
||||
|
||||
// Call the base class method
|
||||
//asynMotorAxis::report(fp, level);
|
||||
}
|
||||
|
||||
|
||||
asynStatus EL734Axis::move(double position, int relative, double minVelocity, double maxVelocity, double acceleration)
|
||||
{
|
||||
asynStatus status;
|
||||
//static const char *functionName = "EL734Axis::move";
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
|
||||
// status = sendAccelAndVelocity(acceleration, maxVelocity);
|
||||
|
||||
if (relative) {
|
||||
position += this->position;
|
||||
}
|
||||
oredMSR = 0;
|
||||
homing = 0;
|
||||
sprintf(command, "p %d %.3f", axisNo_, position/1000.);
|
||||
status = pC_->transactController(command,reply);
|
||||
next_poll = -1;
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus EL734Axis::home(double minVelocity, double maxVelocity, double acceleration, int forwards)
|
||||
{
|
||||
asynStatus status;
|
||||
//static const char *functionName = "EL734Axis::home";
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
|
||||
// status = sendAccelAndVelocity(acceleration, maxVelocity);
|
||||
|
||||
sprintf(command, "R %d", axisNo_);
|
||||
homing = 1;
|
||||
next_poll= -1;
|
||||
status = pC_->transactController(command,reply);
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus EL734Axis::moveVelocity(double minVelocity, double maxVelocity, double acceleration)
|
||||
{
|
||||
asynStatus status;
|
||||
//static const char *functionName = "EL734Axis::moveVelocity";
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
|
||||
// asynPrint(pasynUser_, ASYN_TRACE_FLOW,
|
||||
// "%s: minVelocity=%f, maxVelocity=%f, acceleration=%f\n",
|
||||
// functionName, minVelocity, maxVelocity, acceleration);
|
||||
|
||||
|
||||
if (maxVelocity > 0.) {
|
||||
/* This is a positive move */
|
||||
sprintf(command, "FF %d", axisNo_);
|
||||
} else {
|
||||
/* This is a negative move */
|
||||
sprintf(command, "FB %d", axisNo_);
|
||||
}
|
||||
status = pC_->transactController(command,reply);
|
||||
next_poll = -1;
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus EL734Axis::stop(double acceleration )
|
||||
{
|
||||
asynStatus status;
|
||||
//static const char *functionName = "EL734Axis::stop";
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
|
||||
sprintf(command, "S %d", axisNo_);
|
||||
status = pC_->transactController(command,reply);
|
||||
errlogPrintf("Sent STOP on Axis %d\n", axisNo_);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus EL734Axis::setPosition(double position)
|
||||
{
|
||||
asynStatus status;
|
||||
//static const char *functionName = "EL734Axis::setPosition";
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
|
||||
sprintf(command, "P %d %f", axisNo_, position/1000.);
|
||||
status = pC_->transactController(command,reply);
|
||||
next_poll = -1;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus EL734Axis::setClosedLoop(bool closedLoop)
|
||||
{
|
||||
//static const char *functionName = "EL734Axis::setClosedLoop";
|
||||
|
||||
/*
|
||||
This belongs into the Kingdom of Electronics.
|
||||
We do not do this.
|
||||
*/
|
||||
|
||||
return asynError;
|
||||
}
|
||||
|
||||
/** Polls the axis.
|
||||
* This function reads the motor position, the limit status, the home status, the moving status,
|
||||
* and the drive power-on status.
|
||||
* It calls setIntegerParam() and setDoubleParam() for each item that it polls,
|
||||
* and then calls callParamCallbacks() at the end.
|
||||
* \param[out] moving A flag that is set indicating that the axis is moving (true) or done (false). */
|
||||
asynStatus EL734Axis::poll(bool *moving)
|
||||
{
|
||||
int msr;
|
||||
asynStatus comStatus;
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
|
||||
|
||||
// protect against excessive polling
|
||||
if(time(NULL) < next_poll){
|
||||
*moving = false;
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
// Read the current motor position
|
||||
sprintf(command,"u %d", axisNo_);
|
||||
comStatus = pC_->transactController(command,reply);
|
||||
if(comStatus) goto skip;
|
||||
if(strstr(reply,"*ES") != NULL){
|
||||
*moving = false;
|
||||
setIntegerParam(pC_->motorStatusDone_, true);
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
goto skip;
|
||||
} else if(strstr(reply,"?BSY") != NULL){
|
||||
*moving = true;
|
||||
setIntegerParam(pC_->motorStatusDone_, false);
|
||||
goto skip;
|
||||
}
|
||||
sscanf(reply,"%lf", &position);
|
||||
//errlogPrintf("Axis %d, reply %s, position %lf\n", axisNo_, reply, position);
|
||||
setDoubleParam(pC_->motorPosition_, position*1000);
|
||||
//setDoubleParam(pC_->motorEncoderPosition_, position);
|
||||
|
||||
|
||||
// Read the moving status of this motor
|
||||
sprintf(command,"msr %d",axisNo_);
|
||||
comStatus = pC_->transactController(command,reply);
|
||||
if(comStatus) goto skip;
|
||||
sscanf(reply,"%x",&msr);
|
||||
// errlogPrintf("Axis %d, reply %s, msr %d, position = %lf\n",
|
||||
// axisNo_, reply, msr, position);
|
||||
oredMSR |= msr;
|
||||
if( (msr & 0x1) == 0){
|
||||
// done: check for trouble
|
||||
//errlogPrintf("Axis %d finished\n", axisNo_);
|
||||
next_poll = time(NULL)+IDLEPOLL;
|
||||
if(oredMSR & 0x10){
|
||||
setIntegerParam(pC_->motorStatusLowLimit_, true);
|
||||
} else {
|
||||
setIntegerParam(pC_->motorStatusLowLimit_, false);
|
||||
}
|
||||
if(oredMSR & 0x20){
|
||||
setIntegerParam(pC_->motorStatusHighLimit_, true);
|
||||
} else {
|
||||
setIntegerParam(pC_->motorStatusHighLimit_, false);
|
||||
}
|
||||
if(homing){
|
||||
setIntegerParam(pC_->motorStatusAtHome_, true);
|
||||
}
|
||||
setIntegerParam(pC_->motorStatusProblem_, false);
|
||||
if(oredMSR &0x1000){
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
errlogSevPrintf(errlogMajor, "Air cushion problem on %d", axisNo_);
|
||||
}
|
||||
if(oredMSR &0x80){
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
errlogSevPrintf(errlogMajor, "Positioning fault at %d", axisNo_);
|
||||
}
|
||||
*moving = false;
|
||||
setIntegerParam(pC_->motorStatusDone_, true);
|
||||
} else {
|
||||
*moving = true;
|
||||
next_poll = -1;
|
||||
setIntegerParam(pC_->motorStatusDone_, false);
|
||||
}
|
||||
|
||||
skip:
|
||||
setIntegerParam(pC_->motorStatusProblem_, comStatus ? 1:0);
|
||||
callParamCallbacks();
|
||||
return comStatus ? asynError : asynSuccess;
|
||||
}
|
||||
|
||||
/** Code for iocsh registration */
|
||||
static const iocshArg EL734CreateControllerArg0 = {"Port name", iocshArgString};
|
||||
static const iocshArg EL734CreateControllerArg1 = {"EL734 port name", iocshArgString};
|
||||
static const iocshArg EL734CreateControllerArg2 = {"Number of axes", iocshArgInt};
|
||||
static const iocshArg * const EL734CreateControllerArgs[] = {&EL734CreateControllerArg0,
|
||||
&EL734CreateControllerArg1,
|
||||
&EL734CreateControllerArg2};
|
||||
static const iocshFuncDef EL734CreateControllerDef = {"EL734CreateController", 3, EL734CreateControllerArgs};
|
||||
static void EL734CreateContollerCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
EL734CreateController(args[0].sval, args[1].sval, args[2].ival);
|
||||
}
|
||||
|
||||
static void EL734Register(void)
|
||||
{
|
||||
iocshRegister(&EL734CreateControllerDef, EL734CreateContollerCallFunc);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
epicsExportRegistrar(EL734Register);
|
||||
}
|
57
sinqEPICSApp/src/EL734Driver.h
Normal file
57
sinqEPICSApp/src/EL734Driver.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
FILENAME... EL734Driver.h
|
||||
USAGE... Motor driver support for the PSI EL734 controller.
|
||||
|
||||
Mark Koennecke
|
||||
February 2013
|
||||
|
||||
*/
|
||||
|
||||
#include "asynMotorController.h"
|
||||
#include "asynMotorAxis.h"
|
||||
|
||||
#define MAX_EL734_AXES 12
|
||||
#define COMLEN 80
|
||||
|
||||
class EL734Axis : public asynMotorAxis
|
||||
{
|
||||
public:
|
||||
/* These are the methods we override from the base class */
|
||||
EL734Axis(class EL734Controller *pC, int axis);
|
||||
void report(FILE *fp, int level);
|
||||
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration);
|
||||
asynStatus moveVelocity(double min_velocity, double max_velocity, double acceleration);
|
||||
asynStatus home(double min_velocity, double max_velocity, double acceleration, int forwards);
|
||||
asynStatus stop(double acceleration);
|
||||
asynStatus poll(bool *moving);
|
||||
asynStatus setPosition(double position);
|
||||
asynStatus setClosedLoop(bool closedLoop);
|
||||
|
||||
private:
|
||||
EL734Controller *pC_; /**< Pointer to the asynMotorController to which this axis belongs.
|
||||
* Abbreviated because it is used very frequently */
|
||||
int oredMSR;
|
||||
double position;
|
||||
int homing;
|
||||
time_t next_poll;
|
||||
|
||||
friend class EL734Controller;
|
||||
};
|
||||
|
||||
class EL734Controller : public asynMotorController {
|
||||
public:
|
||||
EL734Controller(const char *portName, const char *EL734PortName, int numAxes);
|
||||
|
||||
void report(FILE *fp, int level);
|
||||
EL734Axis* getAxis(asynUser *pasynUser);
|
||||
EL734Axis* getAxis(int axisNo);
|
||||
|
||||
friend class EL734Axis;
|
||||
private:
|
||||
asynUser *pasynUserController_;
|
||||
|
||||
asynStatus transactController(char command[COMLEN], char reply[COMLEN]);
|
||||
|
||||
void switchRemote();
|
||||
|
||||
};
|
46
sinqEPICSApp/src/Makefile
Normal file
46
sinqEPICSApp/src/Makefile
Normal file
@ -0,0 +1,46 @@
|
||||
TOP=../..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
#----------------------------------------
|
||||
# ADD MACRO DEFINITIONS AFTER THIS LINE
|
||||
#=============================
|
||||
|
||||
#=============================
|
||||
# Build the IOC application
|
||||
|
||||
PROD_IOC = sinqEPICS
|
||||
# sinqEPICS.dbd will be created and installed
|
||||
DBD += sinqEPICS.dbd
|
||||
|
||||
# sinqEPICS.dbd will be made up from these files:
|
||||
sinqEPICS_DBD += base.dbd
|
||||
|
||||
# Include dbd files from all support applications:
|
||||
sinqEPICS_DBD += sinq.dbd
|
||||
sinqEPICS_DBD += pmacAsynIPPort.dbd pmacAsynMotorPort.dbd
|
||||
|
||||
# Add all the support libraries needed by this IOC
|
||||
sinqEPICS_LIBS += motor asyn std anc350 anc350AsynMotor
|
||||
|
||||
# sinqEPICS_registerRecordDeviceDriver.cpp derives from sinqEPICS.dbd
|
||||
sinqEPICS_SRCS += sinqEPICS_registerRecordDeviceDriver.cpp
|
||||
sinqEPICS_SRCS += EL734Driver.cpp devScalerEL737.c pmacAsynIPPort.c
|
||||
sinqEPICS_SRCS += pmacController.cpp pmacAxis.cpp
|
||||
|
||||
|
||||
# Build the main IOC entry point on workstation OSs.
|
||||
sinqEPICS_SRCS_DEFAULT += sinqEPICSMain.cpp
|
||||
sinqEPICS_SRCS_vxWorks += -nil-
|
||||
|
||||
# Add support from base/src/vxWorks if needed
|
||||
#sinqEPICS_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary
|
||||
|
||||
# Finally link to the EPICS Base libraries
|
||||
sinqEPICS_LIBS += $(EPICS_BASE_IOC_LIBS)
|
||||
|
||||
#===========================
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
#----------------------------------------
|
||||
# ADD RULES AFTER THIS LINE
|
||||
|
437
sinqEPICSApp/src/devScalerEL737.c
Normal file
437
sinqEPICSApp/src/devScalerEL737.c
Normal file
@ -0,0 +1,437 @@
|
||||
/**
|
||||
* scaler device support for the PSI EL737 counter box.
|
||||
* The EL737 has modes: preset timer, preset monitor.
|
||||
* The EL7373 has also the option to set a threshold on a monitor.
|
||||
* The EL737 has a maximum of 8 monitors which can be read.
|
||||
* The up to 64 channels of the scaler record will be used as such:
|
||||
* 1 = Time*1000
|
||||
* 2-9 monitor values
|
||||
* 10 extended status. Annotates rate low, paused conditions...
|
||||
*
|
||||
* The preset values are used as such:
|
||||
* 1 = Time*1000 or preset monitor
|
||||
* 2 = mode switch: 0 = preset time, > 0 preset monitor
|
||||
* 2 = threshold monitor
|
||||
* 3 = threshold monitor count
|
||||
*
|
||||
* This is less then ideal. But it is a workaround for the fact that the scalar record
|
||||
* does not do all the tricks the EL737 knows:
|
||||
* - Thresholding
|
||||
* - two count modes
|
||||
* A better solution would be to extend the scalar record to have a proper status and count
|
||||
* mode field. And threshold control fileds too. But this is much more work, breaks
|
||||
* compatability with the scalar record completely and thus was not done for now.
|
||||
*
|
||||
* The driver will run a separate thread which does all the
|
||||
* communication.
|
||||
*
|
||||
* Mark Koennecke, February 2013
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <asynOctetSyncIO.h>
|
||||
#include <epicsEvent.h>
|
||||
#include <epicsThread.h>
|
||||
#include <epicsTime.h>
|
||||
#include <epicsExport.h>
|
||||
#include <errlog.h>
|
||||
#include <callback.h>
|
||||
#include <recSup.h>
|
||||
#include <devSup.h>
|
||||
#include <cantProceed.h>
|
||||
|
||||
#include <scalerRecord.h>
|
||||
#include <devScaler.h>
|
||||
#include <asynEpicsUtils.h>
|
||||
|
||||
|
||||
/* dset functions */
|
||||
static long el737_init_record(struct scalerRecord *psr, CALLBACK *pcallback);
|
||||
static long el737_reset(scalerRecord *psr);
|
||||
static long el737_read(scalerRecord *psr, unsigned long *val);
|
||||
static long el737_write_preset(scalerRecord *psr, int signal, unsigned long val);
|
||||
static long el737_arm(scalerRecord *psr, int val);
|
||||
static long el737_done(scalerRecord *psr);
|
||||
|
||||
/* thread function which runs the device */
|
||||
static void el737Thread(void *param);
|
||||
|
||||
#define MODE 1
|
||||
#define TIMER 0
|
||||
#define MONITOR 1
|
||||
#define THRESHMON 2
|
||||
#define THRESHVAL 3
|
||||
#define NCOUNT 10
|
||||
#define COMLEN 132
|
||||
|
||||
SCALERDSET devScalerEL737 = {
|
||||
7,
|
||||
NULL,
|
||||
NULL,
|
||||
el737_init_record,
|
||||
NULL,
|
||||
el737_reset,
|
||||
el737_read,
|
||||
el737_write_preset,
|
||||
el737_arm,
|
||||
el737_done
|
||||
};
|
||||
epicsExportAddress(dset, devScalerEL737);
|
||||
|
||||
typedef struct {
|
||||
unsigned long presets[13];
|
||||
unsigned long values[NCOUNT];
|
||||
unsigned int countCommand;
|
||||
unsigned int counting;
|
||||
unsigned int sendThreshold;
|
||||
scalerRecord *psr;
|
||||
epicsEventId wakeUp;
|
||||
asynUser *asynContext;
|
||||
CALLBACK *pcallback;
|
||||
}EL737priv;
|
||||
|
||||
static void dummyAsynCallback(asynUser *pasynUser)
|
||||
{
|
||||
}
|
||||
|
||||
static long el737_init_record(scalerRecord *psr, CALLBACK *pcallback)
|
||||
{
|
||||
EL737priv *priv = NULL;
|
||||
char *port, *userParam;
|
||||
int signal, status, reason;
|
||||
size_t in, out;
|
||||
asynUser *dummyUser;
|
||||
char command[80], reply[80];
|
||||
|
||||
/**
|
||||
* initalize record fields
|
||||
*/
|
||||
psr->nch = NCOUNT;
|
||||
psr->g1 = scalerG1_Y;
|
||||
psr->g2 = scalerG1_Y;
|
||||
psr->g3 = scalerG1_Y;
|
||||
psr->g4 = scalerG1_Y;
|
||||
psr->freq = 1000;
|
||||
psr->cont = scalerCONT_OneShot;
|
||||
|
||||
/*
|
||||
* private data structure
|
||||
*/
|
||||
priv = callocMustSucceed(1,sizeof(EL737priv), "devScalerEL737 init_record()");
|
||||
priv->psr = psr;
|
||||
priv->wakeUp = epicsEventCreate(epicsEventEmpty);
|
||||
priv->pcallback = pcallback;
|
||||
psr->dpvt = priv;
|
||||
|
||||
/*
|
||||
* Hook up with device
|
||||
*/
|
||||
dummyUser = pasynManager->createAsynUser(dummyAsynCallback,0);
|
||||
status = pasynEpicsUtils->parseLink(dummyUser, &psr->out,
|
||||
&port, &signal, &userParam);
|
||||
if (status != asynSuccess) {
|
||||
errlogPrintf("devScalerEL737::init_record %s bad link %s\n",
|
||||
psr->name, dummyUser->errorMessage);
|
||||
psr->pact = 1;
|
||||
return 0;
|
||||
}
|
||||
status = pasynOctetSyncIO->connect(port, 0, &priv->asynContext, NULL);
|
||||
if (status) {
|
||||
asynPrint(dummyUser, ASYN_TRACE_ERROR,
|
||||
"%s: cannot connect to EL734 controller\n",
|
||||
"el737_init_scaler");
|
||||
psr->pact = 1;
|
||||
return 0;
|
||||
}
|
||||
pasynOctetSyncIO->setOutputEos(priv->asynContext,"\r",strlen("\r"));
|
||||
pasynOctetSyncIO->setInputEos(priv->asynContext,"\r",strlen("\r"));
|
||||
priv->asynContext->timeout = 1.0;
|
||||
strcpy(command,"RMT 1");
|
||||
pasynOctetSyncIO->writeRead(priv->asynContext, command, strlen(command),
|
||||
reply,sizeof(reply), 1.,&out,&in,&reason);
|
||||
strcpy(command,"ECHO 2");
|
||||
pasynOctetSyncIO->writeRead(priv->asynContext, command, strlen(command),
|
||||
reply,sizeof(reply), 1.,&out,&in,&reason);
|
||||
pasynManager->freeAsynUser(dummyUser);
|
||||
|
||||
|
||||
/*
|
||||
start the thread which actually runs the device
|
||||
*/
|
||||
epicsThreadCreate("EL737",
|
||||
epicsThreadPriorityMedium,
|
||||
epicsThreadStackMedium,
|
||||
el737Thread,
|
||||
priv);
|
||||
|
||||
//errlogPrintf("EL7373 thread started \n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long el737_reset(scalerRecord *psr)
|
||||
{
|
||||
unsigned int i;
|
||||
EL737priv *priv;
|
||||
|
||||
priv = (EL737priv *)psr->dpvt;
|
||||
for(i = 0; i < NCOUNT; i++){
|
||||
priv->values[i] = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long el737_read(scalerRecord *psr, unsigned long *val)
|
||||
{
|
||||
unsigned int i;
|
||||
EL737priv *priv;
|
||||
|
||||
priv = (EL737priv *)psr->dpvt;
|
||||
for(i = 0; i < NCOUNT; i++){
|
||||
val[i] = priv->values[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long el737_write_preset(scalerRecord *psr, int signal, unsigned long val)
|
||||
{
|
||||
EL737priv *priv;
|
||||
|
||||
//errlogPrintf("Setting preset %d to %ld\n", signal, val);
|
||||
|
||||
priv = (EL737priv *)psr->dpvt;
|
||||
if(signal >= 0 && signal < 13){
|
||||
priv->presets[signal] = val;
|
||||
}
|
||||
if(signal == THRESHVAL){
|
||||
priv->sendThreshold = 1;
|
||||
epicsEventSignal(priv->wakeUp);
|
||||
//errlogPrintf("Setting threshold \n");
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long el737_arm(scalerRecord *psr, int val)
|
||||
{
|
||||
EL737priv *priv;
|
||||
|
||||
priv = (EL737priv *)psr->dpvt;
|
||||
priv->countCommand = val;
|
||||
|
||||
//errlogPrintf("Sending arm %d \n", val);
|
||||
|
||||
epicsEventSignal(priv->wakeUp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long el737_done(scalerRecord *psr)
|
||||
{
|
||||
EL737priv *priv;
|
||||
priv = (EL737priv *)psr->dpvt;
|
||||
|
||||
// errlogPrintf("Calling done with %d\n", psr->cnt);
|
||||
if(priv->counting && psr->cnt == 0){
|
||||
priv->countCommand = 0;
|
||||
epicsEventSignal(priv->wakeUp);
|
||||
}
|
||||
|
||||
if(priv->counting || priv->countCommand == 1){
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static asynStatus el737_transactCommand(asynUser *asynContext, char command[COMLEN],char reply[COMLEN])
|
||||
{
|
||||
asynStatus status;
|
||||
size_t in, out;
|
||||
int reason;
|
||||
char myCommand[COMLEN];
|
||||
|
||||
pasynOctetSyncIO->flush(asynContext);
|
||||
|
||||
status = pasynOctetSyncIO->writeRead(asynContext, command, strlen(command),
|
||||
reply,COMLEN, 1.,&out,&in,&reason);
|
||||
if(status == asynSuccess){
|
||||
if(strstr(reply,"?OF") != NULL){
|
||||
strcpy(myCommand,"RMT 1");
|
||||
status = pasynOctetSyncIO->writeRead(asynContext, myCommand, strlen(myCommand),
|
||||
reply,COMLEN, 1.,&out,&in,&reason);
|
||||
strcpy(myCommand,"ECHO 2");
|
||||
status = pasynOctetSyncIO->writeRead(asynContext, myCommand, strlen(myCommand),
|
||||
reply,COMLEN, 1.,&out,&in,&reason);
|
||||
return pasynOctetSyncIO->writeRead(asynContext, command, strlen(command),
|
||||
reply,COMLEN, 1.,&out,&in,&reason);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static asynStatus sendStop(EL737priv *priv)
|
||||
{
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
//errlogPrintf("Sending stop\n");
|
||||
strcpy(command,"S");
|
||||
return el737_transactCommand(priv->asynContext,command,reply);
|
||||
}
|
||||
|
||||
static void runEvents(EL737priv *priv)
|
||||
{
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
int status;
|
||||
|
||||
if(priv->sendThreshold == 1){
|
||||
sprintf(command,"DL %d %d", (int)priv->presets[THRESHMON],
|
||||
(int)priv->presets[THRESHVAL]);
|
||||
//errlogPrintf("Sending threshold command %s\n", command);
|
||||
status = el737_transactCommand(priv->asynContext,command,reply);
|
||||
sprintf(command,"DR %d", (int)priv->presets[THRESHMON]);
|
||||
status = el737_transactCommand(priv->asynContext,command,reply);
|
||||
if(status == asynSuccess){
|
||||
priv->sendThreshold = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//errlogPrintf("Doing a count command: %d\n", priv->countCommand);
|
||||
if(priv->countCommand == 1){
|
||||
if(priv->presets[MODE] > 0) {
|
||||
/* preset monitor */
|
||||
sprintf(command,"MP %d", (int)priv->presets[MONITOR]);
|
||||
//errlogPrintf("Starting preset monitor\n");
|
||||
} else {
|
||||
/* preset time */
|
||||
sprintf(command,"TP %f", priv->presets[TIMER]/1000.);
|
||||
//errlogPrintf("Starting preset timer\n");
|
||||
}
|
||||
status = el737_transactCommand(priv->asynContext,command,reply);
|
||||
if(status == asynSuccess){
|
||||
priv->counting = 1;
|
||||
}
|
||||
} else {
|
||||
if(priv->counting) {
|
||||
/* Stop */
|
||||
status = sendStop(priv);
|
||||
} else {
|
||||
status = asynSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(status != asynSuccess){
|
||||
errlogPrintf("devScalerEL737::el737Thread communication problem %s\n",
|
||||
priv->asynContext->errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
static void updateValues(EL737priv *priv)
|
||||
{
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
int status;
|
||||
float fTime;
|
||||
long m1, m2 ,m3, m4, m5, m6 ,m7, m8;
|
||||
|
||||
strcpy(command,"RA");
|
||||
status = el737_transactCommand(priv->asynContext,command,reply);
|
||||
if(status != asynSuccess){
|
||||
errlogPrintf("devScalerEL737::el737Thread communication problem %s\n",
|
||||
priv->asynContext->errorMessage);
|
||||
return;
|
||||
}
|
||||
status = sscanf(reply, "%f %ld %ld %ld %ld %ld %ld %ld %ld",
|
||||
&fTime, &m1, &m2, &m3, &m4, &m5, &m6, &m7, &m8);
|
||||
if (status != 9) {
|
||||
/*
|
||||
old form with 4 monitors
|
||||
*/
|
||||
status = sscanf(reply, "%ld %ld %ld %ld %f", &m1, &m2, &m3, &m4, &fTime);
|
||||
if (status != 5) {
|
||||
errlogPrintf("devScalerEL737::el737Thread bad RA reply %s\n",
|
||||
reply);
|
||||
return;
|
||||
}
|
||||
}
|
||||
priv->values[0] = (long)(fTime*1000);
|
||||
priv->values[1] = m1;
|
||||
priv->values[2] = m2;
|
||||
priv->values[3] = m3;
|
||||
priv->values[4] = m4;
|
||||
priv->values[5] = m5;
|
||||
priv->values[6] = m6;
|
||||
priv->values[7] = m7;
|
||||
priv->values[8] = m8;
|
||||
|
||||
}
|
||||
|
||||
static void el737Thread(void *param)
|
||||
{
|
||||
EL737priv *priv = (EL737priv *)param;
|
||||
epicsEventWaitStatus evStatus;
|
||||
double timeout = 60.;
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
asynStatus status;
|
||||
int rs;
|
||||
|
||||
//errlogPrintf("Within EL737 thread \n");
|
||||
|
||||
while(1){
|
||||
evStatus = epicsEventWaitWithTimeout(priv->wakeUp,timeout);
|
||||
//errlogPrintf("EL737 thread woke up with %d\n", evStatus);
|
||||
|
||||
if(evStatus == epicsEventWaitOK) {
|
||||
/*
|
||||
FEAR: we received a command!!!!
|
||||
*/
|
||||
runEvents(priv);
|
||||
if(priv->counting == 1){
|
||||
timeout = .1;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
Just do a poll.....
|
||||
*/
|
||||
|
||||
if(priv->counting && priv->countCommand == 0) {
|
||||
/* Stop */
|
||||
status = sendStop(priv);
|
||||
}
|
||||
|
||||
|
||||
if(priv->counting) {
|
||||
strcpy(command,"RS");
|
||||
status = el737_transactCommand(priv->asynContext,command,reply);
|
||||
if(status != asynSuccess){
|
||||
errlogPrintf("devScalerEL737::el737Thread communication problem %s\n",
|
||||
priv->asynContext->errorMessage);
|
||||
break;
|
||||
}
|
||||
sscanf(reply,"%d",&rs);
|
||||
//errlogPrintf("RS = %d\n", rs);
|
||||
if(rs == 0) {
|
||||
priv->counting = 0;
|
||||
timeout = 60.;
|
||||
priv->values[9] = 0;
|
||||
} else if(rs == 1 || rs == 2){
|
||||
/* counting */
|
||||
priv->values[9] = 1;
|
||||
} else if(rs == 5 || rs == 6){
|
||||
/* low rate */
|
||||
priv->values[9] = 2;
|
||||
} else if(rs == 9 || rs == 13 || rs == 10 || rs == 14){
|
||||
/* low rate */
|
||||
priv->values[9] = 2;
|
||||
}
|
||||
}
|
||||
|
||||
updateValues(priv);
|
||||
|
||||
if(rs == 0){
|
||||
callbackRequest(priv->pcallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
9
sinqEPICSApp/src/el737Record.db
Normal file
9
sinqEPICSApp/src/el737Record.db
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
record(scaler,"")
|
||||
{
|
||||
field(NAME,"NZ:counter")
|
||||
field(DESC,"NARZIS EL737 counter")
|
||||
field(DTYP,"asynScalerEL737")
|
||||
field(OUT,"INST_IO @asyn(cter1,0)"
|
||||
}
|
||||
|
776
sinqEPICSApp/src/pmacAsynIPPort.c
Normal file
776
sinqEPICSApp/src/pmacAsynIPPort.c
Normal file
@ -0,0 +1,776 @@
|
||||
/*
|
||||
NOTES
|
||||
|
||||
This driver is an asyn interpose driver intended for use with pmacAsynMotor to allow communication over ethernet to a PMAC.
|
||||
|
||||
*** Ensure I3=2 and I6=1 on the PMAC before using this driver. ***
|
||||
|
||||
This driver supports sending ascii commands to the PMAC over asyn IP and obtaining the response. The driver uses the PMAC ethernet
|
||||
packets VR_PMAC_GETRESPONSE, VR_PMAC_READREADY and VR_PMAC_GETBUFFER to send commands and get responses. The PMAC may send responses
|
||||
in several different formats.
|
||||
1) PMAC ascii command responses for single/multiple commands (e.g. I113 I114 I130 I131 I133) are in an ACK terminated
|
||||
form as follows (CR seperates the cmd responses):
|
||||
data<CR(13)>data<CR(13)>data<CR(13)><ACK(6)>
|
||||
2) PMAC error responses to ascii commands ARE NOT ACK terminated as follows:
|
||||
<BELL(7)>ERRxxx<CR(13)>
|
||||
3) PMAC may also return the following:
|
||||
<STX(2)>data<CR(13)>
|
||||
|
||||
This driver can send ctrl commands (ctrl B/C/F/G/P/V) to the pmac (using VR_CTRL_REPONSE packet) however because the resulting response
|
||||
data is not terminated as above the driver does not know when all the response data has been received. The response data will therefore
|
||||
only be returned after the asynUser.timeout has expired.
|
||||
|
||||
This driver supports the octet flush method and issues a VR_PMAC_FLUSH to the PMAC.
|
||||
|
||||
This driver does NOT support large buffer transfers (VR_PMAC_WRITEBUFFER, VR_FWDOWNLOAD) or set/get of DPRAM (VR_PMAC_SETMEM etc) or changing
|
||||
comms setup (VR_IPADDRESS, VR_PMAC_PORT)
|
||||
|
||||
|
||||
REVISION HISTORY
|
||||
|
||||
10 Aug 07 - Pete Leicester - Diamond Light Source
|
||||
Modified to handle responses other than those ending in <ACK> (e.g. errors) - No longer used asyn EOS.
|
||||
|
||||
9 Aug 07 - Pete Leicester - Diamond Light Source
|
||||
Initial version reliant on asyn EOS to return <ACK> terminated responses.
|
||||
|
||||
2 Feb 09 - Matthew Pearson - Diamond Light Source
|
||||
Ported to work with Asyn 4-10. Still compiles with pre Asyn4-10 versions but does not work.
|
||||
Also added new config function, pmacAsynIPPortConfigureEos(), to be used when disabling
|
||||
low level EOS handling in the Asyn IP layer. This new function should be used with Asyn 4-10 and above
|
||||
(it is not compatible with Asyn 4-9).
|
||||
*/
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <cantProceed.h>
|
||||
#include <epicsAssert.h>
|
||||
#include <epicsStdio.h>
|
||||
#include <epicsString.h>
|
||||
#include <iocsh.h>
|
||||
|
||||
#include <epicsExport.h>
|
||||
#include "asynDriver.h"
|
||||
#include "asynOctet.h"
|
||||
#include "pmacAsynIPPort.h"
|
||||
#include "asynInterposeEos.h"
|
||||
#include "drvAsynIPPort.h"
|
||||
#include "epicsThread.h"
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
#define ETHERNET_DATA_SIZE 1492
|
||||
#define MAX_BUFFER_SIZE 2097152
|
||||
#define INPUT_SIZE (ETHERNET_DATA_SIZE+1) /* +1 to allow space to add terminating ACK */
|
||||
#define STX '\2'
|
||||
#define CTRLB '\2'
|
||||
#define CTRLC '\3'
|
||||
#define ACK '\6'
|
||||
#define CTRLF '\6'
|
||||
#define BELL '\7'
|
||||
#define CTRLG '\7'
|
||||
#define CTRLP '\16'
|
||||
#define CTRLV '\22'
|
||||
#define CTRLX '\24'
|
||||
|
||||
/* PMAC ethernet command structure */
|
||||
#pragma pack(1)
|
||||
typedef struct tagEthernetCmd
|
||||
{
|
||||
unsigned char RequestType;
|
||||
unsigned char Request;
|
||||
unsigned short wValue;
|
||||
unsigned short wIndex;
|
||||
unsigned short wLength; /* length of bData */
|
||||
unsigned char bData[ETHERNET_DATA_SIZE];
|
||||
} ethernetCmd;
|
||||
#pragma pack()
|
||||
|
||||
#define ETHERNET_CMD_HEADER ( sizeof(ethernetCmd) - ETHERNET_DATA_SIZE )
|
||||
|
||||
/* PMAC ethernet commands - RequestType field */
|
||||
#define VR_UPLOAD 0xC0
|
||||
#define VR_DOWNLOAD 0x40
|
||||
|
||||
/* PMAC ethernet commands - Request field */
|
||||
#define VR_PMAC_SENDLINE 0xB0
|
||||
#define VR_PMAC_GETLINE 0xB1
|
||||
#define VR_PMAC_FLUSH 0xB3
|
||||
#define VR_PMAC_GETMEM 0xB4
|
||||
#define VR_PMAC_SETMEN 0xB5
|
||||
#define VR_PMAC_SETBIT 0xBA
|
||||
#define VR_PMAC_SETBITS 0xBB
|
||||
#define VR_PMAC_PORT 0xBE
|
||||
#define VR_PMAC_GETRESPONSE 0xBF
|
||||
#define VR_PMAC_READREADY 0xC2
|
||||
#define VR_CTRL_RESPONSE 0xC4
|
||||
#define VR_PMAC_GETBUFFER 0xC5
|
||||
#define VR_PMAC_WRITEBUFFER 0xC6
|
||||
#define VR_PMAC_WRITEERROR 0xC7
|
||||
#define VR_FWDOWNLOAD 0xCB
|
||||
#define VR_IPADDRESS 0xE0
|
||||
|
||||
/* PMAC control character commands (VR_CTRL_RESPONSE cmd) */
|
||||
static char ctrlCommands[] = { CTRLB,CTRLC,CTRLF,CTRLG,CTRLP,CTRLV };
|
||||
|
||||
typedef struct pmacPvt {
|
||||
char *portName;
|
||||
int addr;
|
||||
asynInterface pmacInterface;
|
||||
asynOctet *poctet; /* The methods we're overriding */
|
||||
void *octetPvt;
|
||||
asynUser *pasynUser; /* For connect/disconnect reporting */
|
||||
ethernetCmd *poutCmd;
|
||||
ethernetCmd *pinCmd;
|
||||
char *inBuf;
|
||||
unsigned int inBufHead;
|
||||
unsigned int inBufTail;
|
||||
} pmacPvt;
|
||||
|
||||
/*
|
||||
Notes on asyn
|
||||
use asynUser.timeout to specify I/O request timeouts in seconds
|
||||
asynStatus may return asynSuccess(0),asynTimeout(1),asynOverflow(2) or asynError(3)
|
||||
eomReason may return ASYN_EOM_CNT (1:Request count reached), ASYN_EOM_EOS (2:End of String detected), ASYN_EOM_END (4:End indicator detected)
|
||||
asynError indicates that asynUser.errorMessage has been populated by epicsSnprintf().
|
||||
*/
|
||||
|
||||
/* Connect/disconnect handling */
|
||||
static void pmacInExceptionHandler(asynUser *pasynUser,asynException exception);
|
||||
|
||||
/* asynOctet methods */
|
||||
static asynStatus writeIt(void *ppvt,asynUser *pasynUser,
|
||||
const char *data,size_t numchars,size_t *nbytesTransfered);
|
||||
static asynStatus readIt(void *ppvt,asynUser *pasynUser,
|
||||
char *data,size_t maxchars,size_t *nbytesTransfered,int *eomReason);
|
||||
static asynStatus flushIt(void *ppvt,asynUser *pasynUser);
|
||||
static asynStatus registerInterruptUser(void *ppvt,asynUser *pasynUser,
|
||||
interruptCallbackOctet callback, void *userPvt,void **registrarPvt);
|
||||
static asynStatus cancelInterruptUser(void *drvPvt,asynUser *pasynUser,
|
||||
void *registrarPvt);
|
||||
static asynStatus setInputEos(void *ppvt,asynUser *pasynUser,
|
||||
const char *eos,int eoslen);
|
||||
static asynStatus getInputEos(void *ppvt,asynUser *pasynUser,
|
||||
char *eos,int eossize ,int *eoslen);
|
||||
static asynStatus setOutputEos(void *ppvt,asynUser *pasynUser,
|
||||
const char *eos,int eoslen);
|
||||
static asynStatus getOutputEos(void *ppvt,asynUser *pasynUser,
|
||||
char *eos,int eossize,int *eoslen);
|
||||
|
||||
/* Declare asynOctet here, and assign functions later on
|
||||
(compatible with both Asyn 4-10 and pre 4-10 versions).*/
|
||||
static asynOctet octet;
|
||||
|
||||
static asynStatus readResponse(pmacPvt *pPmacPvt, asynUser *pasynUser, size_t maxchars, size_t *nbytesTransfered, int *eomReason );
|
||||
static int pmacReadReady(pmacPvt *pPmacPvt, asynUser *pasynUser );
|
||||
static int pmacFlush(pmacPvt *pPmacPvt, asynUser *pasynUser );
|
||||
static int pmacAsynIPPortCommon(const char *portName, int addr, pmacPvt **pPmacPvt, asynInterface **plowerLevelInterface, asynUser **pasynUser);
|
||||
static int pmacAsynIPPortConfigureEos(const char *portName,int addr);
|
||||
static asynStatus sendPmacGetBuffer(pmacPvt *pPmacPvt, asynUser *pasynUser, size_t maxchars,size_t *nbytesTransfered);
|
||||
|
||||
/**
|
||||
* Function that first initialises an Asyn IP port and then the PMAC Asyn IP interpose layer.
|
||||
* It is a wrapper for drvAsynIPPort::drvAsynIPPortConfigure() and
|
||||
* pmacAsynIPPort::pmacAsynIPPortConfigureEos().
|
||||
*
|
||||
* @param portName The Asyn Port name string.
|
||||
* @param hostInfo The hostname or IP address followed by IP port (eg. 172.23.243.156:1025)
|
||||
* @return status
|
||||
*/
|
||||
epicsShareFunc int pmacAsynIPConfigure(const char *portName, const char *hostInfo)
|
||||
{
|
||||
asynStatus status = 0;
|
||||
|
||||
if ((status = drvAsynIPPortConfigure(portName, hostInfo, 0, 0, 1)) != 0) {
|
||||
printf("pmacAsynIPConfigure: error from drvAsynIPPortConfigure. Port: %s\n", portName);
|
||||
}
|
||||
|
||||
if ((status = pmacAsynIPPortConfigureEos(portName, 0)) != 0) {
|
||||
printf("pmacAsynIPConfigure: error from pmacAsynIPPortConfigureEos. Port: %s\n", portName);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* This reimplements pmacAsynIPPort::pmacAsynIPPortConfigure(), but introduces
|
||||
* EOS handling interpose layer above the PMAC IP layer.
|
||||
*
|
||||
* The setup of the Asyn IP port must set noProcessEos=1 to use this function.
|
||||
*
|
||||
* NOTE: The use of this function is not compatible with versions of Asyn before 4-10.
|
||||
*
|
||||
* @param portName The Asyn port name
|
||||
* @param addr The Asyn address.
|
||||
* @return status
|
||||
*/
|
||||
epicsShareFunc int pmacAsynIPPortConfigureEos(const char *portName,int addr)
|
||||
{
|
||||
pmacPvt *pPmacPvt = NULL;
|
||||
asynInterface *plowerLevelInterface = NULL;
|
||||
asynStatus status = 0;
|
||||
asynUser *pasynUser = NULL;
|
||||
|
||||
status = pmacAsynIPPortCommon(portName, addr, &pPmacPvt, &plowerLevelInterface, &pasynUser);
|
||||
if (status) {
|
||||
asynPrint(pasynUser, ASYN_TRACE_ERROR,
|
||||
"pmacAsynIPPortConfigureEos: error from pmacAsynIPPortCommon %s: %s\n",
|
||||
portName, pasynUser->errorMessage);
|
||||
}
|
||||
|
||||
/*Now interpose EOS handling layer, above the PMAC IP layer.*/
|
||||
asynInterposeEosConfig(portName,addr,1,1);
|
||||
|
||||
asynPrint(pasynUser,ASYN_TRACE_FLOW, "Done pmacAsynIPPortConfigureEos OK\n");
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
epicsShareFunc int pmacAsynIPPortConfigure(const char *portName,int addr)
|
||||
{
|
||||
pmacPvt *pPmacPvt = NULL;
|
||||
asynInterface *plowerLevelInterface = NULL;
|
||||
asynUser *pasynUser = NULL;
|
||||
asynStatus status = 0;
|
||||
|
||||
status = pmacAsynIPPortCommon(portName, addr, &pPmacPvt, &plowerLevelInterface, &pasynUser);
|
||||
|
||||
status = pPmacPvt->poctet->setInputEos(pPmacPvt->octetPvt, pasynUser, "\6", 1);
|
||||
if (status) {
|
||||
asynPrint(pasynUser, ASYN_TRACE_ERROR,
|
||||
"pmacAsynIPPortConfigure: unable to set input EOS on %s: %s\n",
|
||||
portName, pasynUser->errorMessage);
|
||||
return -1;
|
||||
}
|
||||
status = pPmacPvt->poctet->setOutputEos(pPmacPvt->octetPvt, pasynUser, "\r", 1);
|
||||
if (status) {
|
||||
asynPrint(pasynUser, ASYN_TRACE_ERROR,
|
||||
"pmacAsynIPPortConfigure: unable to set output EOS on %s: %s\n",
|
||||
portName, pasynUser->errorMessage);
|
||||
return -1;
|
||||
}
|
||||
|
||||
asynPrint(pasynUser,ASYN_TRACE_FLOW, "Done pmacAsynIPPortConfigure OK\n");
|
||||
return(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common code for both pmacAsynIPPortConfigure and pmacAsynIPPortConfigureEos.
|
||||
* @param portName
|
||||
* @param addr
|
||||
* @param pPmacPvt pointer
|
||||
* @param plowerLevelInterface pointer
|
||||
* @param pasynUser pointer
|
||||
* @return status
|
||||
*/
|
||||
static int pmacAsynIPPortCommon(const char *portName,
|
||||
int addr,
|
||||
pmacPvt **pPmacPvt,
|
||||
asynInterface **plowerLevelInterface,
|
||||
asynUser **pasynUser)
|
||||
{
|
||||
asynStatus status;
|
||||
size_t len;
|
||||
|
||||
/*Assign static asynOctet functions here.*/
|
||||
octet.write = writeIt;
|
||||
octet.read = readIt;
|
||||
octet.flush = flushIt;
|
||||
octet.setInputEos = setInputEos;
|
||||
octet.setOutputEos = setOutputEos;
|
||||
octet.getInputEos = getInputEos;
|
||||
octet.getOutputEos = getOutputEos;
|
||||
octet.registerInterruptUser = registerInterruptUser;
|
||||
octet.cancelInterruptUser = cancelInterruptUser;
|
||||
|
||||
len = sizeof(pmacPvt) + strlen(portName) + 1;
|
||||
*pPmacPvt = callocMustSucceed(1,len,"calloc pPmacPvt error in pmacAsynIPPort::pmacAsynIPPortCommon().");
|
||||
(*pPmacPvt)->portName = (char *)(*pPmacPvt+1);
|
||||
strcpy((*pPmacPvt)->portName,portName);
|
||||
(*pPmacPvt)->addr = addr;
|
||||
(*pPmacPvt)->pmacInterface.interfaceType = asynOctetType;
|
||||
(*pPmacPvt)->pmacInterface.pinterface = &octet;
|
||||
(*pPmacPvt)->pmacInterface.drvPvt = *pPmacPvt;
|
||||
*pasynUser = pasynManager->createAsynUser(0,0);
|
||||
(*pPmacPvt)->pasynUser = *pasynUser;
|
||||
(*pPmacPvt)->pasynUser->userPvt = *pPmacPvt;
|
||||
|
||||
status = pasynManager->connectDevice(*pasynUser,portName,addr);
|
||||
if(status!=asynSuccess) {
|
||||
printf("pmacAsynIPPortConfigure: %s connectDevice failed\n",portName);
|
||||
pasynManager->freeAsynUser(*pasynUser);
|
||||
free(*pPmacPvt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = pasynManager->exceptionCallbackAdd(*pasynUser,pmacInExceptionHandler);
|
||||
if(status!=asynSuccess) {
|
||||
printf("pmacAsynIPPortConfigure: %s exceptionCallbackAdd failed\n",portName);
|
||||
pasynManager->freeAsynUser(*pasynUser);
|
||||
free(*pPmacPvt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = pasynManager->interposeInterface(portName,addr,
|
||||
&(*pPmacPvt)->pmacInterface,&(*plowerLevelInterface));
|
||||
if(status!=asynSuccess) {
|
||||
printf("pmacAsynIPPortConfigure: %s interposeInterface failed\n",portName);
|
||||
pasynManager->exceptionCallbackRemove(*pasynUser);
|
||||
pasynManager->freeAsynUser(*pasynUser);
|
||||
free(*pPmacPvt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
(*pPmacPvt)->poctet = (asynOctet *)(*plowerLevelInterface)->pinterface;
|
||||
(*pPmacPvt)->octetPvt = (*plowerLevelInterface)->drvPvt;
|
||||
|
||||
(*pPmacPvt)->poutCmd = callocMustSucceed(1,sizeof(ethernetCmd),"calloc poutCmd error in pmacAsynIPPort::pmacAsynIPPortCommon().");
|
||||
(*pPmacPvt)->pinCmd = callocMustSucceed(1,sizeof(ethernetCmd),"calloc pinCmd error in pmacAsynIPPort::pmacAsynIPPortCommon().");
|
||||
|
||||
(*pPmacPvt)->inBuf = callocMustSucceed(1,MAX_BUFFER_SIZE,"calloc inBuf error in pmacAsynIPPort::pmacAsynIPPortCommon().");
|
||||
|
||||
(*pPmacPvt)->inBufHead = 0;
|
||||
(*pPmacPvt)->inBufTail = 0;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static void pmacInExceptionHandler(asynUser *pasynUser,asynException exception)
|
||||
{
|
||||
pmacPvt *pPmacPvt = (pmacPvt *)pasynUser->userPvt;
|
||||
asynPrint(pasynUser,ASYN_TRACE_FLOW, "pmacInExceptionHandler\n");
|
||||
|
||||
if (exception == asynExceptionConnect) {
|
||||
pPmacPvt->inBufHead = 0;
|
||||
pPmacPvt->inBufTail = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Read reponse data from PMAC into buffer pmacPvt.inBuf. If there is no data in the asyn buffer then issue
|
||||
pmac GETBUFFER command to get any outstanding data still on the PMAC.
|
||||
*/
|
||||
static asynStatus readResponse(pmacPvt *pPmacPvt, asynUser *pasynUser, size_t maxchars, size_t *nbytesTransfered, int *eomReason )
|
||||
{
|
||||
ethernetCmd* inCmd;
|
||||
asynStatus status = asynSuccess;
|
||||
size_t thisRead = 0;
|
||||
*nbytesTransfered = 0;
|
||||
if (maxchars>INPUT_SIZE) maxchars = INPUT_SIZE;
|
||||
|
||||
asynPrint(pasynUser,ASYN_TRACE_FLOW, "pmacAsynIPPort::readResponse. Performing pPmacPvt->poctet->read().\n");
|
||||
|
||||
status = pPmacPvt->poctet->read(pPmacPvt->octetPvt,
|
||||
pasynUser,pPmacPvt->inBuf,maxchars,&thisRead,eomReason);
|
||||
|
||||
asynPrint(pasynUser,ASYN_TRACE_FLOW, "%s readResponse1 maxchars=%d, thisRead=%d, eomReason=%d, status=%d\n",pPmacPvt->portName, maxchars, thisRead, *eomReason, status);
|
||||
if (status == asynTimeout && thisRead == 0 && pasynUser->timeout>0) {
|
||||
/* failed to read as many characters as required into the input buffer,
|
||||
check for more response data on the PMAC */
|
||||
if ( pmacReadReady(pPmacPvt,pasynUser )) {
|
||||
|
||||
status = sendPmacGetBuffer(pPmacPvt, pasynUser, maxchars, nbytesTransfered);
|
||||
asynPrintIO(pasynUser,ASYN_TRACE_FLOW,(char*)pPmacPvt->pinCmd,ETHERNET_CMD_HEADER,
|
||||
"%s write GETBUFFER\n",pPmacPvt->portName);
|
||||
|
||||
/* We have nothing to return at the moment so read again */
|
||||
status = pPmacPvt->poctet->read(pPmacPvt->octetPvt,
|
||||
pasynUser,pPmacPvt->inBuf,maxchars,&thisRead,eomReason);
|
||||
|
||||
asynPrint(pasynUser,ASYN_TRACE_FLOW, "%s readResponse2 maxchars=%d, thisRead=%d, eomReason=%d, status=%d\n",pPmacPvt->portName, maxchars, thisRead, *eomReason, status);
|
||||
}
|
||||
}
|
||||
|
||||
if (thisRead>0) {
|
||||
if (status == asynTimeout) status = asynSuccess;
|
||||
*nbytesTransfered = thisRead;
|
||||
pPmacPvt->inBufTail = 0;
|
||||
pPmacPvt->inBufHead = thisRead;
|
||||
}
|
||||
|
||||
asynPrint( pasynUser, ASYN_TRACE_FLOW, "pmacAsynIPPort::readResponse. END\n" );
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Send ReadReady command to PMAC to discover if there is any data to read from it.
|
||||
Returns: 0 - no data available
|
||||
1 - data available
|
||||
*/
|
||||
static int pmacReadReady(pmacPvt *pPmacPvt, asynUser *pasynUser )
|
||||
{
|
||||
ethernetCmd cmd;
|
||||
unsigned char data[2] = {0};
|
||||
asynStatus status;
|
||||
size_t thisRead = 0;
|
||||
size_t nbytesTransfered = 0;
|
||||
int eomReason = 0;
|
||||
int retval = 0;
|
||||
|
||||
cmd.RequestType = VR_UPLOAD;
|
||||
cmd.Request = VR_PMAC_READREADY;
|
||||
cmd.wValue = 0;
|
||||
cmd.wIndex = 0;
|
||||
cmd.wLength = htons(2);
|
||||
|
||||
status = pPmacPvt->poctet->write(pPmacPvt->octetPvt,
|
||||
pasynUser,(char*)&cmd,ETHERNET_CMD_HEADER,&nbytesTransfered);
|
||||
|
||||
if(status!=asynSuccess) {
|
||||
asynPrintIO(pasynUser,ASYN_TRACE_ERROR,(char*)&cmd,ETHERNET_CMD_HEADER,
|
||||
"%s write pmacReadReady fail\n",pPmacPvt->portName);
|
||||
}
|
||||
|
||||
status = pPmacPvt->poctet->read(pPmacPvt->octetPvt,
|
||||
pasynUser,data,2,&thisRead,&eomReason);
|
||||
|
||||
if(status==asynSuccess) {
|
||||
if (thisRead==2 && data[0] != 0) {
|
||||
retval = 1;
|
||||
}
|
||||
asynPrintIO(pasynUser,ASYN_TRACE_FLOW,data,thisRead,
|
||||
"%s read pmacReadReady OK thisRead=%d\n",pPmacPvt->portName,thisRead);
|
||||
} else {
|
||||
asynPrint(pasynUser,ASYN_TRACE_ERROR, "%s read pmacReadReady failed status=%d,retval=%d",pPmacPvt->portName,status,retval);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
Send Flush command to PMAC and wait for confirmation ctrlX to be returned.
|
||||
Returns: 0 - failed
|
||||
1 - success
|
||||
*/
|
||||
static int pmacFlush(pmacPvt *pPmacPvt, asynUser *pasynUser )
|
||||
{
|
||||
ethernetCmd cmd;
|
||||
unsigned char data[2];
|
||||
asynStatus status = asynSuccess;
|
||||
size_t thisRead;
|
||||
size_t nbytesTransfered = 0;
|
||||
int eomReason = 0;
|
||||
int retval = 0;
|
||||
|
||||
cmd.RequestType = VR_DOWNLOAD;
|
||||
cmd.Request = VR_PMAC_FLUSH;
|
||||
cmd.wValue = 0;
|
||||
cmd.wIndex = 0;
|
||||
cmd.wLength = 0;
|
||||
|
||||
status = pPmacPvt->poctet->write(pPmacPvt->octetPvt,
|
||||
pasynUser,(char*)&cmd,ETHERNET_CMD_HEADER,&nbytesTransfered);
|
||||
|
||||
if(status!=asynSuccess) {
|
||||
asynPrintIO(pasynUser,ASYN_TRACE_ERROR,(char*)&cmd,ETHERNET_CMD_HEADER,
|
||||
"%s write pmacFlush fail\n",pPmacPvt->portName);
|
||||
}
|
||||
|
||||
/* read flush acknowledgement character */
|
||||
/* NB we don't check what the character is (manual sais ctrlX, we get 0x40 i.e.VR_DOWNLOAD) */
|
||||
status = pPmacPvt->poctet->read(pPmacPvt->octetPvt,
|
||||
pasynUser,data,1,&thisRead,&eomReason);
|
||||
|
||||
if(status==asynSuccess) {
|
||||
asynPrint(pasynUser,ASYN_TRACE_FLOW, "%s read pmacFlush OK\n",pPmacPvt->portName);
|
||||
retval = 1;
|
||||
} else {
|
||||
asynPrint(pasynUser,ASYN_TRACE_ERROR, "%s read pmacFlush failed - thisRead=%d, eomReason=%d, status=%d\n",pPmacPvt->portName, thisRead, eomReason, status);
|
||||
}
|
||||
|
||||
pPmacPvt->inBufTail = 0;
|
||||
pPmacPvt->inBufHead = 0;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/* asynOctet methods */
|
||||
|
||||
|
||||
/* This function sends either a ascii string command to the PMAC using VR_PMAC_GETRESPONSE or a single control
|
||||
character (ctrl B/C/F/G/P/V) using VR_CTRL_RESPONSE
|
||||
*/
|
||||
static asynStatus writeIt(void *ppvt,asynUser *pasynUser,
|
||||
const char *data,size_t numchars,size_t *nbytesTransfered)
|
||||
{
|
||||
asynStatus status;
|
||||
ethernetCmd* outCmd;
|
||||
pmacPvt *pPmacPvt = (pmacPvt *)ppvt;
|
||||
size_t nbytesActual = 0;
|
||||
asynPrint( pasynUser, ASYN_TRACE_FLOW, "pmacAsynIPPort::writeIt\n" );
|
||||
assert(pPmacPvt);
|
||||
|
||||
/* NB currently we assume control characters arrive as individual characters/calls to this routine. Idealy we should probably
|
||||
scan the data buffer for control commands and do PMAC_GETRESPONSE and CTRL_RESPONSE commands as necessary, */
|
||||
outCmd = pPmacPvt->poutCmd;
|
||||
if (numchars==1 && strchr(ctrlCommands,data[0])){
|
||||
outCmd->RequestType = VR_UPLOAD;
|
||||
outCmd->Request = VR_CTRL_RESPONSE;
|
||||
outCmd->wValue = data[0];
|
||||
outCmd->wIndex = 0;
|
||||
outCmd->wLength = htons(0);
|
||||
status = pPmacPvt->poctet->write(pPmacPvt->octetPvt,
|
||||
pasynUser,(char*)pPmacPvt->poutCmd,ETHERNET_CMD_HEADER,&nbytesActual);
|
||||
*nbytesTransfered = (nbytesActual==ETHERNET_CMD_HEADER) ? numchars : 0;
|
||||
} else {
|
||||
if (numchars > ETHERNET_DATA_SIZE) {
|
||||
/* NB large data should probably be sent using PMAC_WRITEBUFFER which isnt implemented yet - for the moment just truncate */
|
||||
numchars = ETHERNET_DATA_SIZE;
|
||||
asynPrint( pasynUser, ASYN_TRACE_ERROR, "writeIt - ERROR TRUNCATED\n" );
|
||||
}
|
||||
outCmd->RequestType = VR_DOWNLOAD;
|
||||
outCmd->Request = VR_PMAC_GETRESPONSE;
|
||||
outCmd->wValue = 0;
|
||||
outCmd->wIndex = 0;
|
||||
outCmd->wLength = htons(numchars);
|
||||
memcpy(outCmd->bData,data,numchars);
|
||||
status = pPmacPvt->poctet->write(pPmacPvt->octetPvt,
|
||||
pasynUser,(char*)pPmacPvt->poutCmd,numchars+ETHERNET_CMD_HEADER,&nbytesActual);
|
||||
*nbytesTransfered = (nbytesActual>ETHERNET_CMD_HEADER) ? (nbytesActual - ETHERNET_CMD_HEADER) : 0;
|
||||
}
|
||||
|
||||
asynPrintIO(pasynUser,ASYN_TRACE_FLOW,(char*)pPmacPvt->poutCmd,numchars+ETHERNET_CMD_HEADER,
|
||||
"%s writeIt\n",pPmacPvt->portName);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/* This function reads data using read() into a local buffer and then look for message terminating characters and returns a complete
|
||||
response (or times out), adding on ACK if neccessary.
|
||||
The PMAC command response may be any of the following:-
|
||||
data<CR>data<CR>....data<CR><ACK>
|
||||
<BELL>data<CR> e.g. an error <BELL>ERRxxx<CR>
|
||||
<STX>data<CR>
|
||||
(NB asyn EOS only allows one message terminator to be specified. We add on ACK for the EOS layer above.)
|
||||
*/
|
||||
static asynStatus readIt(void *ppvt,asynUser *pasynUser,
|
||||
char *data,size_t maxchars,size_t *nbytesTransfered,int *eomReason)
|
||||
{
|
||||
pmacPvt *pPmacPvt = (pmacPvt *)ppvt;
|
||||
asynStatus status = asynSuccess;
|
||||
size_t thisRead = 0;
|
||||
size_t nRead = 0;
|
||||
int bell = 0;
|
||||
int initialRead = 1;
|
||||
ethernetCmd* inCmd;
|
||||
|
||||
asynPrint( pasynUser, ASYN_TRACE_FLOW, "pmacAsynIPPort::readIt. START\n" );
|
||||
assert(pPmacPvt);
|
||||
|
||||
if (maxchars > 0) {
|
||||
for (;;) {
|
||||
if ((pPmacPvt->inBufTail != pPmacPvt->inBufHead)) {
|
||||
*data = pPmacPvt->inBuf[pPmacPvt->inBufTail++];
|
||||
if (*data == BELL || *data == STX) bell = 1;
|
||||
if (*data == '\r' && bell) {
|
||||
/* <BELL>xxxxxx<CR> or <STX>xxxxx<CR> received - its probably an error response (<BELL>ERRxxx<CR>) - assume there is no more response data to come */
|
||||
nRead++; /* make sure the <CR> is passed to the client app */
|
||||
/*Add on ACK, because that's what we expect to be EOS in EOS interpose layer.*/
|
||||
if ((nRead+1)>maxchars) {
|
||||
/*If maxchars is reached overwrite <CR> with ACK, so that no more reads will be done from EOS layer.*/
|
||||
*data = ACK;
|
||||
} else {
|
||||
data++;
|
||||
nRead++;
|
||||
*data = ACK;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (*data == ACK || *data == '\n') {
|
||||
/* <ACK> or <LF> received - assume there is no more response data to come */
|
||||
/* If <LF>, replace with an ACK.*/
|
||||
if (*data == '\n') {
|
||||
*data = ACK;
|
||||
}
|
||||
asynPrint( pasynUser, ASYN_TRACE_FLOW, "Message was terminated with ACK in pmacAsynIPPort::readIt.\n" );
|
||||
/*Pass ACK up to Asyn EOS handling layer.*/
|
||||
data++;
|
||||
nRead++;
|
||||
break;
|
||||
}
|
||||
data++;
|
||||
nRead++;
|
||||
if (nRead >= maxchars) break;
|
||||
continue;
|
||||
}
|
||||
|
||||
asynPrint( pasynUser, ASYN_TRACE_FLOW, "pmacAsynIPPort::readIt. Calling readResponse().\n" );
|
||||
if (!initialRead) {
|
||||
if (pmacReadReady(pPmacPvt, pasynUser)) {
|
||||
status = sendPmacGetBuffer(pPmacPvt, pasynUser, maxchars, nbytesTransfered);
|
||||
}
|
||||
}
|
||||
status = readResponse(pPmacPvt, pasynUser, maxchars-nRead, &thisRead, eomReason);
|
||||
initialRead = 0;
|
||||
if(status!=asynSuccess || thisRead==0) break;
|
||||
}
|
||||
}
|
||||
*nbytesTransfered = nRead;
|
||||
|
||||
asynPrintIO(pasynUser,ASYN_TRACE_FLOW, data, *nbytesTransfered, "%s pmacAsynIPPort readIt nbytesTransfered=%d, eomReason=%d, status=%d\n",pPmacPvt->portName,*nbytesTransfered, *eomReason, status);
|
||||
|
||||
asynPrint( pasynUser, ASYN_TRACE_FLOW, "pmacAsynIPPort::readIt. END\n" );
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static asynStatus sendPmacGetBuffer(pmacPvt *pPmacPvt, asynUser *pasynUser, size_t maxchars,size_t *nbytesTransfered)
|
||||
{
|
||||
asynStatus status = 0;
|
||||
ethernetCmd* inCmd = NULL;
|
||||
|
||||
inCmd = pPmacPvt->pinCmd;
|
||||
inCmd->RequestType = VR_UPLOAD;
|
||||
inCmd->Request = VR_PMAC_GETBUFFER;
|
||||
inCmd->wValue = 0;
|
||||
inCmd->wIndex = 0;
|
||||
inCmd->wLength = htons(maxchars);
|
||||
status = pPmacPvt->poctet->write(pPmacPvt->octetPvt,
|
||||
pasynUser,(char*)pPmacPvt->pinCmd,ETHERNET_CMD_HEADER,nbytesTransfered);
|
||||
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static asynStatus flushIt(void *ppvt,asynUser *pasynUser)
|
||||
{
|
||||
pmacPvt *pPmacPvt = (pmacPvt *)ppvt;
|
||||
asynStatus status = asynSuccess;
|
||||
asynPrint( pasynUser, ASYN_TRACE_FLOW, "pmacAsynIPPort::flushIt\n" );
|
||||
assert(pPmacPvt);
|
||||
|
||||
pmacFlush (pPmacPvt, pasynUser);
|
||||
status = pPmacPvt->poctet->flush(pPmacPvt->octetPvt,pasynUser);
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
static asynStatus registerInterruptUser(void *ppvt,asynUser *pasynUser,
|
||||
interruptCallbackOctet callback, void *userPvt,void **registrarPvt)
|
||||
{
|
||||
pmacPvt *pPmacPvt = (pmacPvt *)ppvt;
|
||||
asynPrint( pasynUser, ASYN_TRACE_FLOW, "pmacAsynIPPort::registerInterruptUser\n" );
|
||||
assert(pPmacPvt);
|
||||
|
||||
return pPmacPvt->poctet->registerInterruptUser(pPmacPvt->octetPvt,
|
||||
pasynUser,callback,userPvt,registrarPvt);
|
||||
}
|
||||
|
||||
static asynStatus cancelInterruptUser(void *drvPvt,asynUser *pasynUser,
|
||||
void *registrarPvt)
|
||||
{
|
||||
pmacPvt *pPmacPvt = (pmacPvt *)drvPvt;
|
||||
asynPrint( pasynUser, ASYN_TRACE_FLOW, "pmacAsynIPPort::cancelInterruptUser\n" );
|
||||
assert(pPmacPvt);
|
||||
|
||||
return pPmacPvt->poctet->cancelInterruptUser(pPmacPvt->octetPvt,
|
||||
pasynUser,registrarPvt);
|
||||
}
|
||||
|
||||
static asynStatus setInputEos(void *ppvt,asynUser *pasynUser,
|
||||
const char *eos,int eoslen)
|
||||
{
|
||||
pmacPvt *pPmacPvt = (pmacPvt *)ppvt;
|
||||
asynPrint( pasynUser, ASYN_TRACE_FLOW, "pmacAsynIPPort::setInputEos\n" );
|
||||
assert(pPmacPvt);
|
||||
|
||||
return pPmacPvt->poctet->setInputEos(pPmacPvt->octetPvt,pasynUser,
|
||||
eos,eoslen);
|
||||
}
|
||||
|
||||
static asynStatus getInputEos(void *ppvt,asynUser *pasynUser,
|
||||
char *eos,int eossize,int *eoslen)
|
||||
{
|
||||
pmacPvt *pPmacPvt = (pmacPvt *)ppvt;
|
||||
asynPrint( pasynUser, ASYN_TRACE_FLOW, "pmacAsynIPPort::getInputEos\n" );
|
||||
assert(pPmacPvt);
|
||||
|
||||
return pPmacPvt->poctet->getInputEos(pPmacPvt->octetPvt,pasynUser,
|
||||
eos,eossize,eoslen);
|
||||
}
|
||||
|
||||
static asynStatus setOutputEos(void *ppvt,asynUser *pasynUser,
|
||||
const char *eos, int eoslen)
|
||||
{
|
||||
pmacPvt *pPmacPvt = (pmacPvt *)ppvt;
|
||||
asynPrint( pasynUser, ASYN_TRACE_FLOW, "pmacAsynIPPort::setOutputEos\n" );
|
||||
assert(pPmacPvt);
|
||||
|
||||
return pPmacPvt->poctet->setOutputEos(pPmacPvt->octetPvt,
|
||||
pasynUser,eos,eoslen);
|
||||
}
|
||||
|
||||
static asynStatus getOutputEos(void *ppvt,asynUser *pasynUser,
|
||||
char *eos,int eossize,int *eoslen)
|
||||
{
|
||||
pmacPvt *pPmacPvt = (pmacPvt *)ppvt;
|
||||
asynPrint( pasynUser, ASYN_TRACE_FLOW, "pmacAsynIPPort::getOutputEos\n" );
|
||||
assert(pPmacPvt);
|
||||
|
||||
return pPmacPvt->poctet->getOutputEos(pPmacPvt->octetPvt,
|
||||
pasynUser,eos,eossize,eoslen);
|
||||
}
|
||||
|
||||
|
||||
/* register pmacAsynIPPortConfigure*/
|
||||
static const iocshArg pmacAsynIPPortConfigArg0 =
|
||||
{ "portName", iocshArgString };
|
||||
static const iocshArg pmacAsynIPPortConfigArg1 =
|
||||
{ "addr", iocshArgInt };
|
||||
static const iocshArg *pmacAsynIPPortConfigArgs[] =
|
||||
{&pmacAsynIPPortConfigArg0,&pmacAsynIPPortConfigArg1};
|
||||
static const iocshFuncDef pmacAsynIPPortConfigFuncDef =
|
||||
{"pmacAsynIPPortConfigure", 2, pmacAsynIPPortConfigArgs};
|
||||
static void pmacAsynIPPortConfigCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
pmacAsynIPPortConfigure(args[0].sval,args[1].ival);
|
||||
}
|
||||
|
||||
/* Register pmacAsynIPPortConfigureEos.*/
|
||||
static const iocshArg pmacAsynIPPortConfigEosArg0 =
|
||||
{ "portName", iocshArgString };
|
||||
static const iocshArg pmacAsynIPPortConfigEosArg1 =
|
||||
{ "addr", iocshArgInt };
|
||||
static const iocshArg *pmacAsynIPPortConfigEosArgs[] =
|
||||
{&pmacAsynIPPortConfigEosArg0,&pmacAsynIPPortConfigEosArg1};
|
||||
static const iocshFuncDef pmacAsynIPPortConfigEosFuncDef =
|
||||
{"pmacAsynIPPortConfigureEos", 2, pmacAsynIPPortConfigEosArgs};
|
||||
static void pmacAsynIPPortConfigEosCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
pmacAsynIPPortConfigureEos(args[0].sval,args[1].ival);
|
||||
}
|
||||
|
||||
|
||||
/* Register pmacAsynIPConfigure.*/
|
||||
static const iocshArg pmacAsynIPConfigureArg0 =
|
||||
{ "portName", iocshArgString };
|
||||
static const iocshArg pmacAsynIPConfigureArg1 =
|
||||
{ "hostInfo", iocshArgString };
|
||||
static const iocshArg *pmacAsynIPConfigureArgs[] =
|
||||
{&pmacAsynIPConfigureArg0,&pmacAsynIPConfigureArg1};
|
||||
static const iocshFuncDef pmacAsynIPConfigureFuncDef =
|
||||
{"pmacAsynIPConfigure", 2, pmacAsynIPConfigureArgs};
|
||||
static void pmacAsynIPConfigureCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
pmacAsynIPConfigure(args[0].sval,args[1].sval);
|
||||
}
|
||||
|
||||
static void pmacAsynIPPortRegister(void)
|
||||
{
|
||||
static int firstTime = 1;
|
||||
if (firstTime) {
|
||||
firstTime = 0;
|
||||
iocshRegister(&pmacAsynIPPortConfigFuncDef, pmacAsynIPPortConfigCallFunc);
|
||||
iocshRegister(&pmacAsynIPPortConfigEosFuncDef, pmacAsynIPPortConfigEosCallFunc);
|
||||
iocshRegister(&pmacAsynIPConfigureFuncDef, pmacAsynIPConfigureCallFunc);
|
||||
}
|
||||
}
|
||||
epicsExportRegistrar(pmacAsynIPPortRegister);
|
12
sinqEPICSApp/src/pmacAsynIPPort.dbd
Normal file
12
sinqEPICSApp/src/pmacAsynIPPort.dbd
Normal file
@ -0,0 +1,12 @@
|
||||
registrar(asynRegister)
|
||||
registrar(pmacAsynIPPortRegister)
|
||||
|
||||
#
|
||||
# The following ties this to EPICS records.
|
||||
# Applications which wish to use ASYN without EPICS
|
||||
# records can comment out the following lines.
|
||||
#include "asynRecord.dbd"
|
||||
|
||||
#include "devEpics.dbd"
|
||||
|
||||
#driver(drvAsyn)
|
18
sinqEPICSApp/src/pmacAsynIPPort.h
Normal file
18
sinqEPICSApp/src/pmacAsynIPPort.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef asynInterposePmac_H
|
||||
#define asynInterposePmac_H
|
||||
|
||||
#include <shareLib.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
epicsShareFunc int pmacAsynIPPortConfigure(const char *portName,int addr);
|
||||
epicsShareFunc int pmacAsynIPConfigure(const char *portName, const char *hostInfo);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* asynInterposePmac_H */
|
2
sinqEPICSApp/src/pmacAsynMotorPort.dbd
Normal file
2
sinqEPICSApp/src/pmacAsynMotorPort.dbd
Normal file
@ -0,0 +1,2 @@
|
||||
registrar(pmacControllerRegister)
|
||||
|
505
sinqEPICSApp/src/pmacAxis.cpp
Normal file
505
sinqEPICSApp/src/pmacAxis.cpp
Normal file
@ -0,0 +1,505 @@
|
||||
/********************************************
|
||||
* pmacAxis.cpp
|
||||
*
|
||||
* PMAC Asyn motor based on the
|
||||
* asynMotorAxis class.
|
||||
*
|
||||
* Matthew Pearson
|
||||
* 23 May 2012
|
||||
*
|
||||
* Substantially modified for use at SINQ at PSI.
|
||||
* The thing with the PMACS is that they can be programmed.
|
||||
* This affects also the commands they understand.
|
||||
* Our PMACS also do not seem to like to return multiple replies.....
|
||||
*
|
||||
* I use a starting flag to catch a peculiar feature of our PMAC implementation:
|
||||
* when the motor is hung, it wont start. I check for this and cause an alarm.
|
||||
*
|
||||
* Another mode where the motor is in trouble is if it stays too long in status 5 or 6.
|
||||
* I check and cause an alarm in this state too.
|
||||
*
|
||||
* Mark Koennecke, February 2013
|
||||
********************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <epicsTime.h>
|
||||
#include <epicsThread.h>
|
||||
#include <epicsExport.h>
|
||||
#include <epicsExit.h>
|
||||
#include <epicsString.h>
|
||||
#include <iocsh.h>
|
||||
#include <errlog.h>
|
||||
|
||||
#include "pmacController.h"
|
||||
|
||||
|
||||
#define MULT 1000.
|
||||
|
||||
extern "C" void shutdownCallback(void *pPvt)
|
||||
{
|
||||
pmacController *pC = static_cast<pmacController *>(pPvt);
|
||||
|
||||
pC->lock();
|
||||
pC->shuttingDown_ = 1;
|
||||
pC->unlock();
|
||||
}
|
||||
|
||||
// These are the pmacAxis class methods
|
||||
pmacAxis::pmacAxis(pmacController *pC, int axisNo)
|
||||
: asynMotorAxis(pC, axisNo),
|
||||
pC_(pC)
|
||||
{
|
||||
static const char *functionName = "pmacAxis::pmacAxis";
|
||||
|
||||
pC_->debugFlow(functionName);
|
||||
|
||||
//Initialize non-static data members
|
||||
setpointPosition_ = 0.0;
|
||||
encoderPosition_ = 0.0;
|
||||
currentVelocity_ = 0.0;
|
||||
velocity_ = 0.0;
|
||||
accel_ = 0.0;
|
||||
highLimit_ = 0.0;
|
||||
lowLimit_ = 0.0;
|
||||
scale_ = 1;
|
||||
previous_position_ = 0.0;
|
||||
previous_direction_ = 0;
|
||||
axisErrorCount = 0;
|
||||
startTime = 0;
|
||||
status6Time = 0;
|
||||
starting = 0;
|
||||
homing = 0;
|
||||
|
||||
/* Set an EPICS exit handler that will shut down polling before asyn kills the IP sockets */
|
||||
epicsAtExit(shutdownCallback, pC_);
|
||||
|
||||
//Do an initial poll to get some values from the PMAC
|
||||
if (getAxisInitialStatus() != asynSuccess) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s: getAxisInitialStatus failed to return asynSuccess. Controller: %s, Axis: %d.\n", functionName, pC_->portName, axisNo_);
|
||||
}
|
||||
|
||||
|
||||
callParamCallbacks();
|
||||
|
||||
/* Wake up the poller task which will make it do a poll,
|
||||
* updating values for this axis to use the new resolution (stepSize_) */
|
||||
pC_->wakeupPoller();
|
||||
|
||||
}
|
||||
|
||||
asynStatus pmacAxis::getAxisInitialStatus(void)
|
||||
{
|
||||
char command[pC_->PMAC_MAXBUF_];
|
||||
char response[pC_->PMAC_MAXBUF_];
|
||||
int cmdStatus = 0;
|
||||
double low_limit = 0.0;
|
||||
double high_limit = 0.0;
|
||||
int nvals = 0;
|
||||
int limVal;
|
||||
|
||||
static const char *functionName = "pmacAxis::getAxisInitialStatus";
|
||||
|
||||
sprintf(command, "Q%2.2d00", axisNo_);
|
||||
cmdStatus = pC_->lowLevelWriteRead(command, response);
|
||||
nvals = sscanf(response, "%lf", &scale_);
|
||||
if (cmdStatus || nvals != 1) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s: Error: failed to read scale_factor on axis %d.\n", functionName, axisNo_);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
sprintf(command, "I%d13", axisNo_);
|
||||
cmdStatus = pC_->lowLevelWriteRead(command, response);
|
||||
nvals = sscanf(response, "%d", &limVal);
|
||||
if (cmdStatus || nvals != 1) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s: Error: failed to read high limit on axis %d.\n", functionName, axisNo_);
|
||||
return asynError;
|
||||
}
|
||||
high_limit = ((double)limVal)*scale_;
|
||||
|
||||
sprintf(command, "I%d14", axisNo_);
|
||||
cmdStatus = pC_->lowLevelWriteRead(command, response);
|
||||
nvals = sscanf(response, "%d", &limVal);
|
||||
if (cmdStatus || nvals != 1) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s: Error: failed to read low limit on axis %d.\n", functionName, axisNo_);
|
||||
return asynError;
|
||||
}
|
||||
low_limit = ((double)limVal)*scale_;
|
||||
|
||||
char message[132];
|
||||
sprintf(message,"scale_factor = %lf, high = %lf, low = %lf", scale_,high_limit, low_limit);
|
||||
pC_->debugFlow(message);
|
||||
|
||||
setDoubleParam(pC_->motorLowLimit_, low_limit);
|
||||
setDoubleParam(pC_->motorHighLimit_, high_limit);
|
||||
setIntegerParam(pC_->motorStatusHasEncoder_, 1);
|
||||
|
||||
// Enable the axis. After startup, the axis are disabled on the controller...
|
||||
sprintf(command, "M%2.2d14=1", axisNo_);
|
||||
cmdStatus = pC_->lowLevelWriteRead(command, response);
|
||||
if (cmdStatus ) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s: Error: enaabling axis %d failed.\n", functionName, axisNo_);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
callParamCallbacks();
|
||||
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
|
||||
pmacAxis::~pmacAxis()
|
||||
{
|
||||
//Destructor
|
||||
}
|
||||
|
||||
|
||||
asynStatus pmacAxis::move(double position, int relative, double min_velocity, double max_velocity, double acceleration)
|
||||
{
|
||||
asynStatus status = asynError;
|
||||
static const char *functionName = "pmacAxis::move";
|
||||
double realPosition;
|
||||
|
||||
pC_->debugFlow(functionName);
|
||||
|
||||
char command[128] = {0};
|
||||
char response[32] = {0};
|
||||
|
||||
pC_->debugFlow(functionName);
|
||||
|
||||
if(relative){
|
||||
realPosition = previous_position_ + position/MULT;
|
||||
} else {
|
||||
realPosition = position/MULT;
|
||||
}
|
||||
startTime = time(NULL);
|
||||
status6Time = 0;
|
||||
starting = 1;
|
||||
|
||||
sprintf( command, "P%2.2d23=0 Q%2.2d01=%12.4f M%2.2d=1", axisNo_, axisNo_, realPosition, axisNo_);
|
||||
|
||||
status = pC_->lowLevelWriteRead(command, response);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
|
||||
asynStatus pmacAxis::home(double min_velocity, double max_velocity, double acceleration, int forwards)
|
||||
{
|
||||
asynStatus status = asynError;
|
||||
char command[128] = {0};
|
||||
char response[128] = {0};
|
||||
static const char *functionName = "pmacAxis::home";
|
||||
|
||||
pC_->debugFlow(functionName);
|
||||
|
||||
sprintf(command, "M%2.2d=9", axisNo_);
|
||||
|
||||
status = pC_->lowLevelWriteRead(command, response);
|
||||
homing = 1;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus pmacAxis::moveVelocity(double min_velocity, double max_velocity, double acceleration)
|
||||
{
|
||||
asynStatus status = asynError;
|
||||
char command[128] = {0};
|
||||
char response[32] = {0};
|
||||
static const char *functionName = "pmacAxis::moveVelocity";
|
||||
|
||||
pC_->debugFlow(functionName);
|
||||
|
||||
return asynError; // can we do this, actually?
|
||||
|
||||
// status = pC_->lowLevelWriteRead(command, response);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
asynStatus pmacAxis::setPosition(double position)
|
||||
{
|
||||
//int status = 0;
|
||||
static const char *functionName = "pmacAxis::setPosition";
|
||||
|
||||
pC_->debugFlow(functionName);
|
||||
|
||||
// Cannot do this.
|
||||
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
asynStatus pmacAxis::stop(double acceleration)
|
||||
{
|
||||
asynStatus status = asynError;
|
||||
static const char *functionName = "pmacAxis::stopAxis";
|
||||
|
||||
pC_->debugFlow(functionName);
|
||||
|
||||
char command[128] = {0};
|
||||
char response[32] = {0};
|
||||
|
||||
sprintf( command, "M%2.2d=8", axisNo_ );
|
||||
|
||||
status = pC_->lowLevelWriteRead(command, response);
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus pmacAxis::poll(bool *moving)
|
||||
{
|
||||
int status = 0;
|
||||
static const char *functionName = "pmacAxis::poll";
|
||||
char message[132];
|
||||
|
||||
sprintf(message, "%s: Polling axis: %d", functionName, this->axisNo_);
|
||||
pC_->debugFlow(message);
|
||||
|
||||
//Now poll axis status
|
||||
if ((status = getAxisStatus(moving)) != asynSuccess) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s: getAxisStatus failed to return asynSuccess. Controller: %s, Axis: %d.\n", functionName, pC_->portName, axisNo_);
|
||||
}
|
||||
|
||||
callParamCallbacks();
|
||||
return status ? asynError : asynSuccess;
|
||||
}
|
||||
|
||||
static char *translateAxisError(int axErr)
|
||||
{
|
||||
switch(axErr){
|
||||
case 0:
|
||||
return strdup("no error");
|
||||
break;
|
||||
case 1:
|
||||
return strdup("limit violation");
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
return strdup("jog error");
|
||||
break;
|
||||
case 5:
|
||||
return strdup("command not allowed");
|
||||
break;
|
||||
case 6:
|
||||
return strdup("watchdog triggere");
|
||||
break;
|
||||
case 7:
|
||||
return strdup("current limit reached");
|
||||
break;
|
||||
case 8:
|
||||
case 9:
|
||||
return strdup("air cushion error");
|
||||
break;
|
||||
case 10:
|
||||
return strdup("MCU limit reached");
|
||||
break;
|
||||
case 11:
|
||||
return strdup("following error triggered");
|
||||
break;
|
||||
case 12:
|
||||
return strdup("EMERGENCY STOP activated");
|
||||
break;
|
||||
case 13:
|
||||
return strdup("Driver electronics error");
|
||||
break;
|
||||
case 15:
|
||||
return strdup("Motor blocked");
|
||||
break;
|
||||
default:
|
||||
return strdup("Unknown axis error");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
asynStatus pmacAxis::getAxisStatus(bool *moving)
|
||||
{
|
||||
char command[pC_->PMAC_MAXBUF_];
|
||||
char response[pC_->PMAC_MAXBUF_];
|
||||
int cmdStatus = 0;;
|
||||
int done = 0;
|
||||
double position = 0;
|
||||
double enc_position = 0;
|
||||
int nvals = 0;
|
||||
int axisProblemFlag = 0;
|
||||
int limitsDisabledBit = 0;
|
||||
epicsUInt32 axErr = 0, axStat = 0, highLim = 0, lowLim= 0;
|
||||
int errorPrintMin = 10000;
|
||||
char message[132], *axMessage;
|
||||
|
||||
|
||||
/* read our status items one by one: our PMAC does not seem to give multiple responses ..*/
|
||||
sprintf(command,"P%2.2d01",axisNo_);
|
||||
cmdStatus = pC_->lowLevelWriteRead(command, response);
|
||||
nvals = sscanf( response, "%d", &axErr);
|
||||
if ( cmdStatus || nvals != 1) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"drvPmacAxisGetStatus: Failed to read axis Error Status: %d\nCommand :%s\nResponse:%s\n",
|
||||
cmdStatus, command, response );
|
||||
}
|
||||
|
||||
sprintf(command,"Q%2.2d10",axisNo_);
|
||||
cmdStatus = pC_->lowLevelWriteRead(command, response);
|
||||
nvals = sscanf( response, "%lf", &position);
|
||||
if ( cmdStatus || nvals != 1) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"drvPmacAxisGetStatus: Failed to read position Status: %d\nCommand :%s\nResponse:%s\n",
|
||||
cmdStatus, command, response );
|
||||
}
|
||||
|
||||
sprintf(command,"P%2.2d00",axisNo_);
|
||||
cmdStatus = pC_->lowLevelWriteRead(command, response);
|
||||
nvals = sscanf( response, "%d", &axStat);
|
||||
if ( cmdStatus || nvals != 1) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"drvPmacAxisGetStatus: Failed to read axis status, Status: %d\nCommand :%s\nResponse:%s\n",
|
||||
cmdStatus, command, response );
|
||||
}
|
||||
|
||||
sprintf(command,"M%2.2d21", axisNo_);
|
||||
cmdStatus = pC_->lowLevelWriteRead(command, response);
|
||||
nvals = sscanf( response, "%d", &highLim);
|
||||
if ( cmdStatus || nvals != 1) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"drvPmacAxisGetStatus: Failed to read high limit flag Status: %d\nCommand :%s\nResponse:%s\n",
|
||||
cmdStatus, command, response );
|
||||
}
|
||||
|
||||
sprintf(command,"M%2.2d22", axisNo_);
|
||||
cmdStatus = pC_->lowLevelWriteRead(command, response);
|
||||
nvals = sscanf( response, "%d", &lowLim);
|
||||
if ( cmdStatus || nvals != 1) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"drvPmacAxisGetStatus: Failed to low limit flag Status: %d\nCommand :%s\nResponse:%s\n",
|
||||
cmdStatus, command, response );
|
||||
}
|
||||
|
||||
|
||||
int direction = 0;
|
||||
|
||||
setDoubleParam(pC_->motorPosition_, position*MULT);
|
||||
setDoubleParam(pC_->motorEncoderPosition_, position*MULT);
|
||||
|
||||
/* Use previous position and current position to calculate direction.*/
|
||||
if ((position - previous_position_) > 0) {
|
||||
direction = 1;
|
||||
} else if (position - previous_position_ == 0.0) {
|
||||
direction = previous_direction_;
|
||||
} else {
|
||||
direction = 0;
|
||||
}
|
||||
setIntegerParam(pC_->motorStatusDirection_, direction);
|
||||
|
||||
/*Store position to calculate direction for next poll.*/
|
||||
previous_position_ = position;
|
||||
previous_direction_ = direction;
|
||||
|
||||
/* are we done? */
|
||||
if((axStat == 0 || axStat == 14 || axStat < 0) && starting == 0 ){
|
||||
done = 1;
|
||||
} else {
|
||||
starting = 0;
|
||||
done = 0;
|
||||
}
|
||||
|
||||
if(starting && time(NULL) > startTime + 10){
|
||||
/*
|
||||
did not start in 10 seconds: messed up: cause an alarm
|
||||
*/
|
||||
done = 1;
|
||||
*moving = false;
|
||||
setIntegerParam(pC_->motorStatusMoving_, false);
|
||||
setIntegerParam(pC_->motorStatusDone_, true);
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
errlogPrintf("Axis %d did not start within 10 seconds!! BROKEN\n", axisNo_);
|
||||
starting = 0;
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
code to check for checking against too long status 6
|
||||
*/
|
||||
if(axStat == 5 || axStat == 6){
|
||||
if(status6Time == 0){
|
||||
status6Time = time(NULL);
|
||||
} else {
|
||||
if(time(NULL) > status6Time + 60){
|
||||
done = 1;
|
||||
*moving = false;
|
||||
setIntegerParam(pC_->motorStatusMoving_, false);
|
||||
setIntegerParam(pC_->motorStatusDone_, true);
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
errlogPrintf("Axis %d stayed in 5,6 for more then 60 seconds BROKEN\n", axisNo_);
|
||||
status6Time = 0;
|
||||
return asynSuccess;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
*moving = true;
|
||||
setIntegerParam(pC_->motorStatusMoving_, true);
|
||||
setIntegerParam(pC_->motorStatusDone_, false);
|
||||
} else {
|
||||
*moving = false;
|
||||
setIntegerParam(pC_->motorStatusMoving_, false);
|
||||
setIntegerParam(pC_->motorStatusDone_, true);
|
||||
if(homing){
|
||||
setIntegerParam(pC_->motorStatusHomed_, done);
|
||||
homing = 0;
|
||||
}
|
||||
}
|
||||
|
||||
sprintf(message,"poll results: axis %d, status %d, axErr %d, done = %d",
|
||||
axisNo_, axStat, axErr, done);
|
||||
pC_->debugFlow(message);
|
||||
|
||||
/* search for trouble */
|
||||
if(highLim){
|
||||
setIntegerParam(pC_->motorStatusHighLimit_, true );
|
||||
} else {
|
||||
setIntegerParam(pC_->motorStatusHighLimit_, false );
|
||||
}
|
||||
if(lowLim){
|
||||
setIntegerParam(pC_->motorStatusLowLimit_, true );
|
||||
} else {
|
||||
setIntegerParam(pC_->motorStatusLowLimit_, false );
|
||||
}
|
||||
if(axErr == 11) {
|
||||
setIntegerParam(pC_->motorStatusFollowingError_,true );
|
||||
} else {
|
||||
setIntegerParam(pC_->motorStatusFollowingError_,false);
|
||||
}
|
||||
/* Set any axis specific general problem bits. */
|
||||
if (axStat < 0 || axErr != 0) {
|
||||
axisProblemFlag = 1;
|
||||
if(axisErrorCount < 10){
|
||||
axMessage = translateAxisError(axErr);
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"drvPmacAxisGetStatus: Axis %d is in deep trouble: axis error code %d, translated: %s:, status code = %d\n",
|
||||
axisNo_, axErr, axMessage, axStat);
|
||||
if(axMessage != NULL){
|
||||
free(axMessage);
|
||||
}
|
||||
axisErrorCount++;
|
||||
} else if (axisErrorCount == 10){
|
||||
asynPrint(pC_->pasynUserSelf,ASYN_TRACE_ERROR, "Suppressing further axis error messages\n");
|
||||
axisErrorCount++;
|
||||
}
|
||||
} else {
|
||||
axisProblemFlag = 0;
|
||||
axisErrorCount = 0;
|
||||
}
|
||||
setIntegerParam(pC_->motorStatusProblem_, axisProblemFlag);
|
||||
|
||||
|
||||
return asynSuccess;
|
||||
}
|
||||
|
61
sinqEPICSApp/src/pmacAxis.h
Normal file
61
sinqEPICSApp/src/pmacAxis.h
Normal file
@ -0,0 +1,61 @@
|
||||
/********************************************
|
||||
* pmacAxis.cpp
|
||||
*
|
||||
* PMAC Asyn motor based on the
|
||||
* asynMotorAxis class.
|
||||
*
|
||||
* Matthew Pearson
|
||||
* 23 May 2012
|
||||
*
|
||||
********************************************/
|
||||
|
||||
#ifndef pmacAxis_H
|
||||
#define pmacAxis_H
|
||||
|
||||
#include "asynMotorController.h"
|
||||
#include "asynMotorAxis.h"
|
||||
|
||||
class pmacController;
|
||||
|
||||
class pmacAxis : public asynMotorAxis
|
||||
{
|
||||
public:
|
||||
/* These are the methods we override from the base class */
|
||||
pmacAxis(pmacController *pController, int axisNo);
|
||||
virtual ~pmacAxis();
|
||||
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration);
|
||||
asynStatus moveVelocity(double min_velocity, double max_velocity, double acceleration);
|
||||
asynStatus home(double min_velocity, double max_velocity, double acceleration, int forwards);
|
||||
asynStatus stop(double acceleration);
|
||||
asynStatus poll(bool *moving);
|
||||
asynStatus setPosition(double position);
|
||||
|
||||
private:
|
||||
pmacController *pC_;
|
||||
|
||||
asynStatus getAxisStatus(bool *moving);
|
||||
asynStatus getAxisInitialStatus(void);
|
||||
|
||||
double setpointPosition_;
|
||||
double encoderPosition_;
|
||||
double currentVelocity_;
|
||||
double velocity_;
|
||||
double accel_;
|
||||
double highLimit_;
|
||||
double lowLimit_;
|
||||
double scale_;
|
||||
double previous_position_;
|
||||
int previous_direction_;
|
||||
int encoder_axis_;
|
||||
int axisErrorCount;
|
||||
|
||||
time_t startTime;
|
||||
time_t status6Time;
|
||||
int starting;
|
||||
int homing;
|
||||
|
||||
friend class pmacController;
|
||||
};
|
||||
|
||||
|
||||
#endif /* pmacAxis_H */
|
651
sinqEPICSApp/src/pmacController.cpp
Normal file
651
sinqEPICSApp/src/pmacController.cpp
Normal file
@ -0,0 +1,651 @@
|
||||
/********************************************
|
||||
* pmacController.cpp
|
||||
*
|
||||
* PMAC Asyn motor based on the
|
||||
* asynMotorController class.
|
||||
*
|
||||
* Matthew Pearson
|
||||
* 23 May 2012
|
||||
*
|
||||
*
|
||||
* Substantially modified for use at SINQ at PSI.
|
||||
* The thing with the PMACS is that they can be programmed.
|
||||
* This affects also the commands they understand.
|
||||
*
|
||||
* Mark Koennecke, February 2013
|
||||
********************************************/
|
||||
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <iostream>
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
#include <epicsTime.h>
|
||||
#include <epicsThread.h>
|
||||
#include <epicsExport.h>
|
||||
#include <epicsString.h>
|
||||
#include <iocsh.h>
|
||||
#include <drvSup.h>
|
||||
#include <registryFunction.h>
|
||||
|
||||
#include "asynOctetSyncIO.h"
|
||||
|
||||
#include "pmacController.h"
|
||||
|
||||
#define MULT 1000.
|
||||
|
||||
static const char *driverName = "pmacController";
|
||||
|
||||
const epicsUInt32 pmacController::PMAC_MAXBUF_ = 1024;
|
||||
const epicsFloat64 pmacController::PMAC_TIMEOUT_ = 5.0;
|
||||
|
||||
const epicsUInt32 pmacController::PMAC_STATUS1_MAXRAPID_SPEED = (0x1<<0);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS1_ALT_CMNDOUT_MODE = (0x1<<1);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS1_SOFT_POS_CAPTURE = (0x1<<2);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS1_ERROR_TRIGGER = (0x1<<3);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS1_FOLLOW_ENABLE = (0x1<<4);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS1_FOLLOW_OFFSET = (0x1<<5);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS1_PHASED_MOTOR = (0x1<<6);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS1_ALT_SRC_DEST = (0x1<<7);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS1_USER_SERVO = (0x1<<8);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS1_USER_PHASE = (0x1<<9);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS1_HOMING = (0x1<<10);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS1_BLOCK_REQUEST = (0x1<<11);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS1_DECEL_ABORT = (0x1<<12);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS1_DESIRED_VELOCITY_ZERO = (0x1<<13);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS1_DATABLKERR = (0x1<<14);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS1_DWELL = (0x1<<15);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS1_INTEGRATE_MODE = (0x1<<16);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS1_MOVE_TIME_ON = (0x1<<17);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS1_OPEN_LOOP = (0x1<<18);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS1_AMP_ENABLED = (0x1<<19);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS1_X_SERVO_ON = (0x1<<20);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS1_POS_LIMIT_SET = (0x1<<21);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS1_NEG_LIMIT_SET = (0x1<<22);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS1_MOTOR_ON = (0x1<<23);
|
||||
|
||||
const epicsUInt32 pmacController::PMAC_STATUS2_IN_POSITION = (0x1<<0);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS2_WARN_FOLLOW_ERR = (0x1<<1);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS2_ERR_FOLLOW_ERR = (0x1<<2);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS2_AMP_FAULT = (0x1<<3);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS2_NEG_BACKLASH = (0x1<<4);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS2_I2T_AMP_FAULT = (0x1<<5);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS2_I2_FOLLOW_ERR = (0x1<<6);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS2_TRIGGER_MOVE = (0x1<<7);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS2_PHASE_REF_ERR = (0x1<<8);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS2_PHASE_SEARCH = (0x1<<9);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS2_HOME_COMPLETE = (0x1<<10);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS2_POS_LIMIT_STOP = (0x1<<11);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS2_DESIRED_STOP = (0x1<<12);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS2_FORE_IN_POS = (0x1<<13);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS2_NA14 = (0x1<<14);
|
||||
const epicsUInt32 pmacController::PMAC_STATUS2_ASSIGNED_CS = (0x1<<15);
|
||||
|
||||
/*Global status ???*/
|
||||
const epicsUInt32 pmacController::PMAC_GSTATUS_CARD_ADDR = (0x1<<0);
|
||||
const epicsUInt32 pmacController::PMAC_GSTATUS_ALL_CARD_ADDR = (0x1<<1);
|
||||
const epicsUInt32 pmacController::PMAC_GSTATUS_RESERVED = (0x1<<2);
|
||||
const epicsUInt32 pmacController::PMAC_GSTATUS_PHASE_CLK_MISS = (0x1<<3);
|
||||
const epicsUInt32 pmacController::PMAC_GSTATUS_MACRO_RING_ERRORCHECK = (0x1<<4);
|
||||
const epicsUInt32 pmacController::PMAC_GSTATUS_MACRO_RING_COMMS = (0x1<<5);
|
||||
const epicsUInt32 pmacController::PMAC_GSTATUS_TWS_PARITY_ERROR = (0x1<<6);
|
||||
const epicsUInt32 pmacController::PMAC_GSTATUS_CONFIG_ERROR = (0x1<<7);
|
||||
const epicsUInt32 pmacController::PMAC_GSTATUS_ILLEGAL_LVAR = (0x1<<8);
|
||||
const epicsUInt32 pmacController::PMAC_GSTATUS_REALTIME_INTR = (0x1<<9);
|
||||
const epicsUInt32 pmacController::PMAC_GSTATUS_FLASH_ERROR = (0x1<<10);
|
||||
const epicsUInt32 pmacController::PMAC_GSTATUS_DPRAM_ERROR = (0x1<<11);
|
||||
const epicsUInt32 pmacController::PMAC_GSTATUS_CKSUM_ACTIVE = (0x1<<12);
|
||||
const epicsUInt32 pmacController::PMAC_GSTATUS_CKSUM_ERROR = (0x1<<13);
|
||||
const epicsUInt32 pmacController::PMAC_GSTATUS_LEADSCREW_COMP = (0x1<<14);
|
||||
const epicsUInt32 pmacController::PMAC_GSTATUS_WATCHDOG = (0x1<<15);
|
||||
const epicsUInt32 pmacController::PMAC_GSTATUS_SERVO_REQ = (0x1<<16);
|
||||
const epicsUInt32 pmacController::PMAC_GSTATUS_DATA_GATHER_START = (0x1<<17);
|
||||
const epicsUInt32 pmacController::PMAC_GSTATUS_RESERVED2 = (0x1<<18);
|
||||
const epicsUInt32 pmacController::PMAC_GSTATUS_DATA_GATHER_ON = (0x1<<19);
|
||||
const epicsUInt32 pmacController::PMAC_GSTATUS_SERVO_ERROR = (0x1<<20);
|
||||
const epicsUInt32 pmacController::PMAC_GSTATUS_CPUTYPE = (0x1<<21);
|
||||
const epicsUInt32 pmacController::PMAC_GSTATUS_REALTIME_INTR_RE = (0x1<<22);
|
||||
const epicsUInt32 pmacController::PMAC_GSTATUS_RESERVED3 = (0x1<<23);
|
||||
|
||||
const epicsUInt32 pmacController::PMAC_HARDWARE_PROB = (PMAC_GSTATUS_MACRO_RING_ERRORCHECK | PMAC_GSTATUS_MACRO_RING_COMMS | PMAC_GSTATUS_REALTIME_INTR | PMAC_GSTATUS_FLASH_ERROR | PMAC_GSTATUS_DPRAM_ERROR | PMAC_GSTATUS_CKSUM_ERROR | PMAC_GSTATUS_WATCHDOG | PMAC_GSTATUS_SERVO_ERROR);
|
||||
|
||||
const epicsUInt32 pmacController::PMAX_AXIS_GENERAL_PROB1 = 0;
|
||||
const epicsUInt32 pmacController::PMAX_AXIS_GENERAL_PROB2 = (PMAC_STATUS2_DESIRED_STOP | PMAC_STATUS2_AMP_FAULT);
|
||||
|
||||
|
||||
//C function prototypes, for the functions that can be called on IOC shell
|
||||
extern "C" {
|
||||
asynStatus pmacCreateController(const char *portName, const char *lowLevelPortName, int lowLevelPortAddress,
|
||||
int numAxes, int movingPollPeriod, int idlePollPeriod);
|
||||
|
||||
asynStatus pmacCreateAxis(const char *pmacName, int axis);
|
||||
asynStatus pmacCreateAxis(const char *pmacName, int numAxis);
|
||||
|
||||
}
|
||||
|
||||
pmacController::pmacController(const char *portName, const char *lowLevelPortName, int lowLevelPortAddress,
|
||||
int numAxes, double movingPollPeriod, double idlePollPeriod)
|
||||
: asynMotorController(portName, numAxes+1, NUM_MOTOR_DRIVER_PARAMS,
|
||||
0, // No additional interfaces
|
||||
0, // No addition interrupt interfaces
|
||||
ASYN_CANBLOCK | ASYN_MULTIDEVICE,
|
||||
1, // autoconnect
|
||||
0, 0) // Default priority and stack size
|
||||
{
|
||||
static const char *functionName = "pmacController::pmacController";
|
||||
|
||||
printf(" Constructor: %s\n", functionName);
|
||||
|
||||
//Initialize non static data members
|
||||
lowLevelPortUser_ = NULL;
|
||||
debugFlag_ = 0;
|
||||
|
||||
pAxes_ = (pmacAxis **)(asynMotorController::pAxes_);
|
||||
|
||||
// Create controller-specific parameters
|
||||
createParam(PMAC_C_CommsErrorString, asynParamInt32, &PMAC_C_CommsError_);
|
||||
|
||||
// Connect our Asyn user to the low level port that is a parameter to this constructor
|
||||
if (lowLevelPortConnect(lowLevelPortName, lowLevelPortAddress, &lowLevelPortUser_, "\006", "\r") != asynSuccess) {
|
||||
printf("%s: Failed to connect to low level asynOctetSyncIO port %s\n", functionName, lowLevelPortName);
|
||||
setIntegerParam(PMAC_C_CommsError_, 1);
|
||||
} else {
|
||||
/* Create the poller thread for this controller
|
||||
* NOTE: at this point the axis objects don't yet exist, but the poller tolerates this */
|
||||
setIntegerParam(PMAC_C_CommsError_, 0);
|
||||
}
|
||||
startPoller(movingPollPeriod, idlePollPeriod, 10);
|
||||
|
||||
callParamCallbacks();
|
||||
|
||||
}
|
||||
|
||||
|
||||
pmacController::~pmacController(void)
|
||||
{
|
||||
//Destructor
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Connect to the underlying low level Asyn port that is used for comms.
|
||||
* This uses the asynOctetSyncIO interface, and also sets the input and output terminators.
|
||||
*/
|
||||
int pmacController::lowLevelPortConnect(const char *port, int addr, asynUser **ppasynUser, char *inputEos, char *outputEos)
|
||||
{
|
||||
asynStatus status = asynSuccess;
|
||||
|
||||
static const char *functionName = "pmacController::lowLevelPortConnect";
|
||||
|
||||
debugFlow(functionName);
|
||||
|
||||
status = pasynOctetSyncIO->connect( port, addr, ppasynUser, NULL);
|
||||
if (status) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"pmacController::motorAxisAsynConnect: unable to connect to port %s\n",
|
||||
port);
|
||||
return status;
|
||||
}
|
||||
|
||||
//Do I want to disconnect below? If the IP address comes up, will the driver recover
|
||||
//if the poller functions are running? Might have to use asynManager->isConnected to
|
||||
//test connection status of low level port (in the pollers). But then autosave
|
||||
//restore doesn't work (and we would save wrong positions). So I need to
|
||||
//have a seperate function(s) to deal with connecting after IOC init.
|
||||
|
||||
status = pasynOctetSyncIO->setInputEos(*ppasynUser, inputEos, strlen(inputEos) );
|
||||
if (status) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"pmacController: unable to set input EOS on %s: %s\n",
|
||||
port, (*ppasynUser)->errorMessage);
|
||||
pasynOctetSyncIO->disconnect(*ppasynUser);
|
||||
//Set my low level pasynUser pointer to NULL
|
||||
*ppasynUser = NULL;
|
||||
return status;
|
||||
}
|
||||
|
||||
status = pasynOctetSyncIO->setOutputEos(*ppasynUser, outputEos, strlen(outputEos));
|
||||
if (status) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"pmacController: unable to set output EOS on %s: %s\n",
|
||||
port, (*ppasynUser)->errorMessage);
|
||||
pasynOctetSyncIO->disconnect(*ppasynUser);
|
||||
//Set my low level pasynUser pointer to NULL
|
||||
*ppasynUser = NULL;
|
||||
return status;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utilty function to print the connected status of the low level asyn port.
|
||||
*/
|
||||
asynStatus pmacController::printConnectedStatus()
|
||||
{
|
||||
asynStatus status = asynSuccess;
|
||||
int asynManagerConnected = 0;
|
||||
|
||||
if (lowLevelPortUser_) {
|
||||
status = pasynManager->isConnected(lowLevelPortUser_, &asynManagerConnected);
|
||||
if (status) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"pmacController: Error calling pasynManager::isConnected.\n");
|
||||
return asynError;
|
||||
} else {
|
||||
printf("pmacController::printConnectedStatus: isConnected: %d\n", asynManagerConnected);
|
||||
}
|
||||
}
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for asynOctetSyncIO write/read functions.
|
||||
* @param command - String command to send.
|
||||
* @response response - String response back.
|
||||
*/
|
||||
asynStatus pmacController::lowLevelWriteRead(const char *command, char *response)
|
||||
{
|
||||
asynStatus status = asynSuccess;
|
||||
|
||||
int eomReason;
|
||||
size_t nwrite = 0;
|
||||
size_t nread = 0;
|
||||
int commsError = 0;
|
||||
static const char *functionName = "pmacController::lowLevelWriteRead";
|
||||
|
||||
debugFlow(functionName);
|
||||
|
||||
if (!lowLevelPortUser_) {
|
||||
setIntegerParam(this->motorStatusCommsError_, 1);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
asynPrint(lowLevelPortUser_, ASYN_TRACEIO_DRIVER, "%s: command: %s\n", functionName, command);
|
||||
debugFlow("Sending: ");
|
||||
debugFlow(command);
|
||||
|
||||
//Make sure the low level port is connected before we attempt comms
|
||||
//Use the controller-wide param PMAC_C_CommsError_
|
||||
getIntegerParam(PMAC_C_CommsError_, &commsError);
|
||||
|
||||
if (!commsError) {
|
||||
status = pasynOctetSyncIO->writeRead(lowLevelPortUser_ ,
|
||||
command, strlen(command),
|
||||
response, PMAC_MAXBUF_,
|
||||
PMAC_TIMEOUT_,
|
||||
&nwrite, &nread, &eomReason );
|
||||
|
||||
if (status) {
|
||||
asynPrint(lowLevelPortUser_, ASYN_TRACE_ERROR, "%s: Error from pasynOctetSyncIO->writeRead. command: %s\n", functionName, command);
|
||||
setIntegerParam(this->motorStatusCommsError_, 1);
|
||||
} else {
|
||||
setIntegerParam(this->motorStatusCommsError_, 0);
|
||||
}
|
||||
}
|
||||
|
||||
asynPrint(lowLevelPortUser_, ASYN_TRACEIO_DRIVER, "%s: response: %s\n", functionName, response);
|
||||
debugFlow("Received: ");
|
||||
debugFlow(response);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void pmacController::debugFlow(const char *message)
|
||||
{
|
||||
if (debugFlag_ == 1) {
|
||||
printf(" %s\n", message);
|
||||
}
|
||||
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s\n", message);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void pmacController::report(FILE *fp, int level)
|
||||
{
|
||||
int axis = 0;
|
||||
pmacAxis *pAxis = NULL;
|
||||
|
||||
fprintf(fp, "pmac motor driver %s, numAxes=%d, moving poll period=%f, idle poll period=%f\n",
|
||||
this->portName, numAxes_, movingPollPeriod_, idlePollPeriod_);
|
||||
|
||||
if (level > 0) {
|
||||
for (axis=0; axis<numAxes_; axis++) {
|
||||
pAxis = getAxis(axis);
|
||||
fprintf(fp, " axis %d\n"
|
||||
" scale = %lf\n",
|
||||
pAxis->axisNo_,
|
||||
pAxis->scale_);
|
||||
}
|
||||
}
|
||||
|
||||
// Call the base class method
|
||||
asynMotorController::report(fp, level);
|
||||
}
|
||||
|
||||
|
||||
asynStatus pmacController::writeFloat64(asynUser *pasynUser, epicsFloat64 value)
|
||||
{
|
||||
int function = pasynUser->reason;
|
||||
asynStatus status = asynError;
|
||||
pmacAxis *pAxis = NULL;
|
||||
char command[64] = {0};
|
||||
char response[64] = {0};
|
||||
double encRatio = 1.0;
|
||||
epicsInt32 encposition = 0;
|
||||
char message[132];
|
||||
|
||||
static const char *functionName = "pmacController::writeFloat64";
|
||||
|
||||
sprintf(message,"%s, reason %d", functionName, function);
|
||||
debugFlow(message);
|
||||
//debugFlow(functionName);
|
||||
|
||||
pAxis = this->getAxis(pasynUser);
|
||||
if (!pAxis) {
|
||||
return asynError;
|
||||
}
|
||||
|
||||
|
||||
/* Set the parameter and readback in the parameter library. */
|
||||
status = pAxis->setDoubleParam(function, value);
|
||||
|
||||
// if (function == motorPosition_) {
|
||||
// /*Set position on motor axis.*/
|
||||
// epicsInt32 position = (epicsInt32) floor(value*32/pAxis->scale_ + 0.5);
|
||||
|
||||
// sprintf(command, "#%dK M%d61=%d*I%d08 M%d62=%d*I%d08",
|
||||
// pAxis->axisNo_,
|
||||
// pAxis->axisNo_, position, pAxis->axisNo_,
|
||||
// pAxis->axisNo_, position, pAxis->axisNo_ );
|
||||
|
||||
// asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
|
||||
// "%s: Set axis %d on controller %s to position %f\n",
|
||||
// functionName, pAxis->axisNo_, portName, value);
|
||||
|
||||
// if ( command[0] != 0 && status == asynSuccess) {
|
||||
// status = lowLevelWriteRead(command, response);
|
||||
// }
|
||||
|
||||
// sprintf(command, "#%dJ/", pAxis->axisNo_);
|
||||
|
||||
// if (command[0] != 0 && status == asynSuccess) {
|
||||
// status = lowLevelWriteRead(command, response);
|
||||
// }
|
||||
|
||||
// /*Now set position on encoder axis, if one is in use.*/
|
||||
|
||||
// if (pAxis->encoder_axis_) {
|
||||
// getDoubleParam(motorEncRatio_, &encRatio);
|
||||
// encposition = (epicsInt32) floor((position*encRatio) + 0.5);
|
||||
|
||||
// sprintf(command, "#%dK M%d61=%d*I%d08 M%d62=%d*I%d08",
|
||||
// pAxis->encoder_axis_,
|
||||
// pAxis->encoder_axis_, encposition, pAxis->encoder_axis_,
|
||||
// pAxis->encoder_axis_, encposition, pAxis->encoder_axis_ );
|
||||
|
||||
// asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
|
||||
// "%s: Set encoder axis %d on controller %s to position %f\n",
|
||||
// functionName, pAxis->axisNo_, portName, value);
|
||||
|
||||
// if (command[0] != 0 && status == asynSuccess) {
|
||||
// status = lowLevelWriteRead(command, response);
|
||||
// }
|
||||
|
||||
// sprintf(command, "#%dJ/", pAxis->encoder_axis_);
|
||||
// //The lowLevelWriteRead will be done at the end of this function.
|
||||
// }
|
||||
|
||||
// /*Now do an update, to get the new position from the controller.*/
|
||||
// bool moving = true;
|
||||
// pAxis->getAxisStatus(&moving);
|
||||
// } else
|
||||
if (function == motorLowLimit_) {
|
||||
sprintf(command, "I%d14=%d", pAxis->axisNo_, (int)(value/pAxis->scale_/MULT));
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
|
||||
"%s: Setting low limit on controller %s, axis %d to %f\n",
|
||||
functionName, portName, pAxis->axisNo_, value);
|
||||
} else if (function == motorHighLimit_) {
|
||||
sprintf(command, "I%d13=%d", pAxis->axisNo_, (int)(value/pAxis->scale_/MULT));
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
|
||||
"%s: Setting high limit on controller %s, axis %d to %f\n",
|
||||
functionName, portName, pAxis->axisNo_, value);
|
||||
}
|
||||
|
||||
//Execute the command.
|
||||
if (command[0] != 0 && status == asynSuccess) {
|
||||
status = lowLevelWriteRead(command, response);
|
||||
}
|
||||
|
||||
//Call base class method
|
||||
//This will handle callCallbacks even if the function was handled here.
|
||||
status = asynMotorController::writeFloat64(pasynUser, value);
|
||||
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
|
||||
asynStatus pmacController::writeInt32(asynUser *pasynUser, epicsInt32 value)
|
||||
{
|
||||
int function = pasynUser->reason;
|
||||
asynStatus status = asynError;
|
||||
pmacAxis *pAxis = NULL;
|
||||
static const char *functionName = "pmacController::writeInt32";
|
||||
|
||||
debugFlow(functionName);
|
||||
|
||||
pAxis = this->getAxis(pasynUser);
|
||||
if (!pAxis) {
|
||||
return asynError;
|
||||
}
|
||||
|
||||
/* Set the parameter and readback in the parameter library. This may be overwritten when we read back the
|
||||
* status at the end, but that's OK */
|
||||
status = pAxis->setIntegerParam(function, value);
|
||||
|
||||
|
||||
//Call base class method
|
||||
//This will handle callCallbacks even if the function was handled here.
|
||||
status = asynMotorController::writeInt32(pasynUser, value);
|
||||
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** Returns a pointer to an pmacAxis object.
|
||||
* Returns NULL if the axis number encoded in pasynUser is invalid.
|
||||
* \param[in] pasynUser asynUser structure that encodes the axis index number. */
|
||||
pmacAxis* pmacController::getAxis(asynUser *pasynUser)
|
||||
{
|
||||
int axisNo = 0;
|
||||
|
||||
getAddress(pasynUser, &axisNo);
|
||||
return getAxis(axisNo);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Returns a pointer to an pmacAxis object.
|
||||
* Returns NULL if the axis number is invalid.
|
||||
* \param[in] axisNo Axis index number. */
|
||||
pmacAxis* pmacController::getAxis(int axisNo)
|
||||
{
|
||||
if ((axisNo < 0) || (axisNo >= numAxes_)) return NULL;
|
||||
return pAxes_[axisNo];
|
||||
}
|
||||
|
||||
|
||||
/** Polls the controller, rather than individual axis.*/
|
||||
asynStatus pmacController::poll()
|
||||
{
|
||||
static const char *functionName = "pmacController::poll";
|
||||
|
||||
debugFlow(functionName);
|
||||
|
||||
if (!lowLevelPortUser_) {
|
||||
setIntegerParam(this->motorStatusCommsError_, 1);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
asynMotorController::poll();
|
||||
|
||||
callParamCallbacks();
|
||||
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*************************************************************************************/
|
||||
/** The following functions have C linkage, and can be called directly or from iocsh */
|
||||
|
||||
extern "C" {
|
||||
|
||||
/**
|
||||
* C wrapper for the pmacController constructor.
|
||||
* See pmacController::pmacController.
|
||||
*
|
||||
*/
|
||||
asynStatus pmacCreateController(const char *portName, const char *lowLevelPortName, int lowLevelPortAddress,
|
||||
int numAxes, int movingPollPeriod, int idlePollPeriod)
|
||||
{
|
||||
|
||||
pmacController *ppmacController
|
||||
= new pmacController(portName, lowLevelPortName, lowLevelPortAddress, numAxes, movingPollPeriod/1000., idlePollPeriod/1000.);
|
||||
ppmacController = NULL;
|
||||
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* C wrapper for the pmacAxis constructor.
|
||||
* See pmacAxis::pmacAxis.
|
||||
*
|
||||
*/
|
||||
asynStatus pmacCreateAxis(const char *pmacName, /* specify which controller by port name */
|
||||
int axis) /* axis number (start from 1). */
|
||||
{
|
||||
pmacController *pC;
|
||||
pmacAxis *pAxis;
|
||||
|
||||
static const char *functionName = "pmacCreateAxis";
|
||||
|
||||
pC = (pmacController*) findAsynPortDriver(pmacName);
|
||||
if (!pC) {
|
||||
printf("%s:%s: Error port %s not found\n",
|
||||
driverName, functionName, pmacName);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
pC->lock();
|
||||
pAxis = new pmacAxis(pC, axis);
|
||||
pAxis = NULL;
|
||||
pC->unlock();
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* C Wrapper function for pmacAxis constructor.
|
||||
* See pmacAxis::pmacAxis.
|
||||
* This function allows creation of multiple pmacAxis objects with axis numbers 1 to numAxes.
|
||||
* @param pmacName Asyn port name for the controller (const char *)
|
||||
* @param numAxes The number of axes to create, starting at 1.
|
||||
*
|
||||
*/
|
||||
asynStatus pmacCreateAxes(const char *pmacName,
|
||||
int numAxes)
|
||||
{
|
||||
pmacController *pC;
|
||||
pmacAxis *pAxis;
|
||||
|
||||
static const char *functionName = "pmacCreateAxis";
|
||||
|
||||
pC = (pmacController*) findAsynPortDriver(pmacName);
|
||||
if (!pC) {
|
||||
printf("%s:%s: Error port %s not found\n",
|
||||
driverName, functionName, pmacName);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
pC->lock();
|
||||
for (int axis=1; axis<=numAxes; axis++) {
|
||||
pAxis = new pmacAxis(pC, axis);
|
||||
pAxis = NULL;
|
||||
}
|
||||
pC->unlock();
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
/* Code for iocsh registration */
|
||||
|
||||
#ifdef vxWorks
|
||||
#else
|
||||
|
||||
/* pmacCreateController */
|
||||
static const iocshArg pmacCreateControllerArg0 = {"Controller port name", iocshArgString};
|
||||
static const iocshArg pmacCreateControllerArg1 = {"Low level port name", iocshArgString};
|
||||
static const iocshArg pmacCreateControllerArg2 = {"Low level port address", iocshArgInt};
|
||||
static const iocshArg pmacCreateControllerArg3 = {"Number of axes", iocshArgInt};
|
||||
static const iocshArg pmacCreateControllerArg4 = {"Moving poll rate (ms)", iocshArgInt};
|
||||
static const iocshArg pmacCreateControllerArg5 = {"Idle poll rate (ms)", iocshArgInt};
|
||||
static const iocshArg * const pmacCreateControllerArgs[] = {&pmacCreateControllerArg0,
|
||||
&pmacCreateControllerArg1,
|
||||
&pmacCreateControllerArg2,
|
||||
&pmacCreateControllerArg3,
|
||||
&pmacCreateControllerArg4,
|
||||
&pmacCreateControllerArg5};
|
||||
static const iocshFuncDef configpmacCreateController = {"pmacCreateController", 6, pmacCreateControllerArgs};
|
||||
static void configpmacCreateControllerCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
pmacCreateController(args[0].sval, args[1].sval, args[2].ival, args[3].ival, args[4].ival, args[5].ival);
|
||||
}
|
||||
|
||||
|
||||
/* pmacCreateAxis */
|
||||
static const iocshArg pmacCreateAxisArg0 = {"Controller port name", iocshArgString};
|
||||
static const iocshArg pmacCreateAxisArg1 = {"Axis number", iocshArgInt};
|
||||
static const iocshArg * const pmacCreateAxisArgs[] = {&pmacCreateAxisArg0,
|
||||
&pmacCreateAxisArg1};
|
||||
static const iocshFuncDef configpmacAxis = {"pmacCreateAxis", 2, pmacCreateAxisArgs};
|
||||
|
||||
static void configpmacAxisCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
pmacCreateAxis(args[0].sval, args[1].ival);
|
||||
}
|
||||
|
||||
/* pmacCreateAxes */
|
||||
static const iocshArg pmacCreateAxesArg0 = {"Controller port name", iocshArgString};
|
||||
static const iocshArg pmacCreateAxesArg1 = {"Num Axes", iocshArgInt};
|
||||
static const iocshArg * const pmacCreateAxesArgs[] = {&pmacCreateAxesArg0,
|
||||
&pmacCreateAxesArg1};
|
||||
static const iocshFuncDef configpmacAxes = {"pmacCreateAxes", 2, pmacCreateAxesArgs};
|
||||
|
||||
static void configpmacAxesCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
pmacCreateAxes(args[0].sval, args[1].ival);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void pmacControllerRegister(void)
|
||||
{
|
||||
iocshRegister(&configpmacCreateController, configpmacCreateControllerCallFunc);
|
||||
iocshRegister(&configpmacAxis, configpmacAxisCallFunc);
|
||||
iocshRegister(&configpmacAxes, configpmacAxesCallFunc);
|
||||
}
|
||||
epicsExportRegistrar(pmacControllerRegister);
|
||||
|
||||
#endif
|
||||
|
||||
} // extern "C"
|
||||
|
140
sinqEPICSApp/src/pmacController.h
Normal file
140
sinqEPICSApp/src/pmacController.h
Normal file
@ -0,0 +1,140 @@
|
||||
/********************************************
|
||||
* pmacController.h
|
||||
*
|
||||
* PMAC Asyn motor based on the
|
||||
* asynMotorController class.
|
||||
*
|
||||
* Matthew Pearson
|
||||
* 23 May 2012
|
||||
*
|
||||
********************************************/
|
||||
|
||||
#ifndef pmacController_H
|
||||
#define pmacController_H
|
||||
|
||||
#include "asynMotorController.h"
|
||||
#include "asynMotorAxis.h"
|
||||
#include "pmacAxis.h"
|
||||
|
||||
#define PMAC_C_GlobalStatusString "PMAC_C_GLOBALSTATUS"
|
||||
#define PMAC_C_CommsErrorString "PMAC_C_COMMSERROR"
|
||||
|
||||
class pmacController : public asynMotorController {
|
||||
|
||||
public:
|
||||
pmacController(const char *portName, const char *lowLevelPortName, int lowLevelPortAddress, int numAxes, double movingPollPeriod,
|
||||
double idlePollPeriod);
|
||||
|
||||
virtual ~pmacController();
|
||||
|
||||
asynStatus printConnectedStatus(void);
|
||||
|
||||
/* These are the methods that we override */
|
||||
asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
|
||||
asynStatus writeFloat64(asynUser *pasynUser, epicsFloat64 value);
|
||||
void report(FILE *fp, int level);
|
||||
pmacAxis* getAxis(asynUser *pasynUser);
|
||||
pmacAxis* getAxis(int axisNo);
|
||||
asynStatus poll();
|
||||
|
||||
|
||||
protected:
|
||||
pmacAxis **pAxes_; /**< Array of pointers to axis objects */
|
||||
|
||||
#define FIRST_PMAC_PARAM PMAC_C_GlobalStatus__
|
||||
int PMAC_C_GlobalStatus_;
|
||||
int PMAC_C_CommsError_;
|
||||
#define LAST_PMAC_PARAM PMAC_C_CommsError__
|
||||
|
||||
private:
|
||||
asynUser* lowLevelPortUser_;
|
||||
epicsUInt32 debugFlag_;
|
||||
asynStatus lowLevelWriteRead(const char *command, char *response);
|
||||
int lowLevelPortConnect(const char *port, int addr, asynUser **ppasynUser, char *inputEos, char *outputEos);
|
||||
void debugFlow(const char *message);
|
||||
|
||||
//static class data members
|
||||
|
||||
static const epicsUInt32 PMAC_MAXBUF_;
|
||||
static const epicsFloat64 PMAC_TIMEOUT_;
|
||||
|
||||
|
||||
static const epicsUInt32 PMAC_STATUS1_MAXRAPID_SPEED;
|
||||
static const epicsUInt32 PMAC_STATUS1_ALT_CMNDOUT_MODE;
|
||||
static const epicsUInt32 PMAC_STATUS1_SOFT_POS_CAPTURE;
|
||||
static const epicsUInt32 PMAC_STATUS1_ERROR_TRIGGER;
|
||||
static const epicsUInt32 PMAC_STATUS1_FOLLOW_ENABLE;
|
||||
static const epicsUInt32 PMAC_STATUS1_FOLLOW_OFFSET;
|
||||
static const epicsUInt32 PMAC_STATUS1_PHASED_MOTOR;
|
||||
static const epicsUInt32 PMAC_STATUS1_ALT_SRC_DEST;
|
||||
static const epicsUInt32 PMAC_STATUS1_USER_SERVO;
|
||||
static const epicsUInt32 PMAC_STATUS1_USER_PHASE;
|
||||
static const epicsUInt32 PMAC_STATUS1_HOMING;
|
||||
static const epicsUInt32 PMAC_STATUS1_BLOCK_REQUEST;
|
||||
static const epicsUInt32 PMAC_STATUS1_DECEL_ABORT;
|
||||
static const epicsUInt32 PMAC_STATUS1_DESIRED_VELOCITY_ZERO;
|
||||
static const epicsUInt32 PMAC_STATUS1_DATABLKERR;
|
||||
static const epicsUInt32 PMAC_STATUS1_DWELL;
|
||||
static const epicsUInt32 PMAC_STATUS1_INTEGRATE_MODE;
|
||||
static const epicsUInt32 PMAC_STATUS1_MOVE_TIME_ON;
|
||||
static const epicsUInt32 PMAC_STATUS1_OPEN_LOOP;
|
||||
static const epicsUInt32 PMAC_STATUS1_AMP_ENABLED;
|
||||
static const epicsUInt32 PMAC_STATUS1_X_SERVO_ON;
|
||||
static const epicsUInt32 PMAC_STATUS1_POS_LIMIT_SET;
|
||||
static const epicsUInt32 PMAC_STATUS1_NEG_LIMIT_SET;
|
||||
static const epicsUInt32 PMAC_STATUS1_MOTOR_ON;
|
||||
|
||||
static const epicsUInt32 PMAC_STATUS2_IN_POSITION;
|
||||
static const epicsUInt32 PMAC_STATUS2_WARN_FOLLOW_ERR;
|
||||
static const epicsUInt32 PMAC_STATUS2_ERR_FOLLOW_ERR;
|
||||
static const epicsUInt32 PMAC_STATUS2_AMP_FAULT;
|
||||
static const epicsUInt32 PMAC_STATUS2_NEG_BACKLASH;
|
||||
static const epicsUInt32 PMAC_STATUS2_I2T_AMP_FAULT;
|
||||
static const epicsUInt32 PMAC_STATUS2_I2_FOLLOW_ERR;
|
||||
static const epicsUInt32 PMAC_STATUS2_TRIGGER_MOVE;
|
||||
static const epicsUInt32 PMAC_STATUS2_PHASE_REF_ERR;
|
||||
static const epicsUInt32 PMAC_STATUS2_PHASE_SEARCH;
|
||||
static const epicsUInt32 PMAC_STATUS2_HOME_COMPLETE;
|
||||
static const epicsUInt32 PMAC_STATUS2_POS_LIMIT_STOP;
|
||||
static const epicsUInt32 PMAC_STATUS2_DESIRED_STOP;
|
||||
static const epicsUInt32 PMAC_STATUS2_FORE_IN_POS;
|
||||
static const epicsUInt32 PMAC_STATUS2_NA14;
|
||||
static const epicsUInt32 PMAC_STATUS2_ASSIGNED_CS;
|
||||
|
||||
/*Global status ???*/
|
||||
static const epicsUInt32 PMAC_GSTATUS_CARD_ADDR;
|
||||
static const epicsUInt32 PMAC_GSTATUS_ALL_CARD_ADDR;
|
||||
static const epicsUInt32 PMAC_GSTATUS_RESERVED;
|
||||
static const epicsUInt32 PMAC_GSTATUS_PHASE_CLK_MISS;
|
||||
static const epicsUInt32 PMAC_GSTATUS_MACRO_RING_ERRORCHECK;
|
||||
static const epicsUInt32 PMAC_GSTATUS_MACRO_RING_COMMS;
|
||||
static const epicsUInt32 PMAC_GSTATUS_TWS_PARITY_ERROR;
|
||||
static const epicsUInt32 PMAC_GSTATUS_CONFIG_ERROR;
|
||||
static const epicsUInt32 PMAC_GSTATUS_ILLEGAL_LVAR;
|
||||
static const epicsUInt32 PMAC_GSTATUS_REALTIME_INTR;
|
||||
static const epicsUInt32 PMAC_GSTATUS_FLASH_ERROR;
|
||||
static const epicsUInt32 PMAC_GSTATUS_DPRAM_ERROR;
|
||||
static const epicsUInt32 PMAC_GSTATUS_CKSUM_ACTIVE;
|
||||
static const epicsUInt32 PMAC_GSTATUS_CKSUM_ERROR;
|
||||
static const epicsUInt32 PMAC_GSTATUS_LEADSCREW_COMP;
|
||||
static const epicsUInt32 PMAC_GSTATUS_WATCHDOG;
|
||||
static const epicsUInt32 PMAC_GSTATUS_SERVO_REQ;
|
||||
static const epicsUInt32 PMAC_GSTATUS_DATA_GATHER_START;
|
||||
static const epicsUInt32 PMAC_GSTATUS_RESERVED2;
|
||||
static const epicsUInt32 PMAC_GSTATUS_DATA_GATHER_ON;
|
||||
static const epicsUInt32 PMAC_GSTATUS_SERVO_ERROR;
|
||||
static const epicsUInt32 PMAC_GSTATUS_CPUTYPE;
|
||||
static const epicsUInt32 PMAC_GSTATUS_REALTIME_INTR_RE;
|
||||
static const epicsUInt32 PMAC_GSTATUS_RESERVED3;
|
||||
|
||||
static const epicsUInt32 PMAC_HARDWARE_PROB;
|
||||
static const epicsUInt32 PMAX_AXIS_GENERAL_PROB1;
|
||||
static const epicsUInt32 PMAX_AXIS_GENERAL_PROB2;
|
||||
|
||||
friend class pmacAxis;
|
||||
|
||||
};
|
||||
|
||||
#define NUM_PMAC_PARAMS (&LAST_PMAC_PARAM - &FIRST_PMAC_PARAM + 1)
|
||||
|
||||
#endif /* pmacController_H */
|
16
sinqEPICSApp/src/sinq.dbd
Normal file
16
sinqEPICSApp/src/sinq.dbd
Normal file
@ -0,0 +1,16 @@
|
||||
#---------------------------------------------
|
||||
# SINQ specific DB definitions
|
||||
#---------------------------------------------
|
||||
registrar(EL734Register)
|
||||
addpath "/usr/local/epics/support/asyn-4-18/dbd"
|
||||
addpath "/usr/local/epics/dbd"
|
||||
addpath "/usr/local/epics/support/motor-6-7/dbd"
|
||||
addpath "/usr/local/epics/support/std-3-1/dbd"
|
||||
addpath "/usr/local/epics/anc350v17/dbd"
|
||||
include "drvAsynIPPort.dbd"
|
||||
include "motorRecord.dbd"
|
||||
include "motorSupport.dbd"
|
||||
include "anc350AsynMotor.dbd"
|
||||
|
||||
include "scalerRecord.dbd"
|
||||
device(scaler,INST_IO,devScalerEL737,"asynScalerEL737")
|
23
sinqEPICSApp/src/sinqEPICSMain.cpp
Normal file
23
sinqEPICSApp/src/sinqEPICSMain.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
/* sinqEPICSMain.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);
|
||||
}
|
Reference in New Issue
Block a user