Initial commit of SINQ EPICS Application

This commit is contained in:
2014-11-03 11:37:50 +01:00
commit fd4b41d131
36 changed files with 15416 additions and 0 deletions

17
Makefile Normal file
View 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
View File

@ -0,0 +1,29 @@
# CONFIG - Load build configuration data
#
# Do not make changes to this file!
# Allow user to override where the build rules come from
RULES = $(EPICS_BASE)
# RELEASE files point to other application tops
include $(TOP)/configure/RELEASE
-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).Common
ifdef T_A
-include $(TOP)/configure/RELEASE.Common.$(T_A)
-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A)
endif
CONFIG = $(RULES)/configure
include $(CONFIG)/CONFIG
# Override the Base definition:
#INSTALL_LOCATION = $(TOP)
# CONFIG_SITE files contain other build configuration settings
include $(TOP)/configure/CONFIG_SITE
-include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).Common
ifdef T_A
-include $(TOP)/configure/CONFIG_SITE.Common.$(T_A)
-include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A)
endif

33
configure/CONFIG_SITE Normal file
View 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
View 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
View 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
View File

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

2
configure/RULES.ioc Normal file
View File

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

2
configure/RULES_DIRS Normal file
View File

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

3
configure/RULES_TOP Normal file
View File

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

6
iocBoot/Makefile Normal file
View File

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

View File

@ -0,0 +1,5 @@
TOP = ../..
include $(TOP)/configure/CONFIG
ARCH = linux-x86
TARGETS = envPaths
include $(TOP)/configure/RULES.ioc

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

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

View 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

File diff suppressed because it is too large Load Diff

43
iocBoot/iocsinqEPICS/st.cmd Executable file
View 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"

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

@ -0,0 +1,22 @@
TOP=../..
include $(TOP)/configure/CONFIG
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
#----------------------------------------------------
# Optimization of db files using dbst (DEFAULT: NO)
#DB_OPT = YES
#----------------------------------------------------
# Create and install (or just install) into <top>/db
# databases, templates, substitutions like this
#DB += xxx.db
#----------------------------------------------------
# If <anyname>.db template is not named <anyname>*.template add
# <anyname>_template = <templatename>
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

8
sinqEPICSApp/Makefile Normal file
View File

@ -0,0 +1,8 @@
TOP = ..
include $(TOP)/configure/CONFIG
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *src*))
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Src*))
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *db*))
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Db*))
include $(TOP)/configure/RULES_DIRS

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

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

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

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

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

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

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

View File

@ -0,0 +1,2 @@
registrar(pmacControllerRegister)

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

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

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

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

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