Compare commits
84 Commits
stream_2_6
...
2.8-alpha
Author | SHA1 | Date | |
---|---|---|---|
b0c8a14071 | |||
4feb14ea35 | |||
2830f07324 | |||
abd8daafc3 | |||
489e783872 | |||
10d1fa8b02 | |||
c8bffebfc6 | |||
97f6beb3ae | |||
b80261ba16 | |||
6a114b2c2c | |||
bf29238762 | |||
13d1fb2ca0 | |||
cc1ab5685c | |||
370b3cd8c4 | |||
3a034f843d | |||
2de8e916b2 | |||
6044edfdd9 | |||
836388cd22 | |||
2685a43760 | |||
0eed3ee9c7 | |||
c5e3e89e44 | |||
1f27983505 | |||
146a8c895f | |||
efd5a0cbcd | |||
2968231b60 | |||
ea2238aa53 | |||
d22e1a5f4a | |||
31189ebecf | |||
d42e5a7a9e | |||
e13aa2a96f | |||
ae021ffeab | |||
5a9d89822f | |||
054b0c0e3f | |||
ac07ac3dcf | |||
0195386606 | |||
0026959b1e | |||
450e88d9a9 | |||
a4888634b6 | |||
46a68831c2 | |||
537d573983 | |||
96450e7010 | |||
31e1243eed | |||
0a90eb3d9c | |||
65f46aec36 | |||
37d14caa5b | |||
384474f6fd | |||
17d8592574 | |||
0936ac7840 | |||
704ece6231 | |||
5c6e98127e | |||
8805437c68 | |||
3acf791409 | |||
0ba674a341 | |||
40c33abac7 | |||
bc67317b0b | |||
06e212c66e | |||
7b314ccffd | |||
1d84986ee8 | |||
2ca8a129f7 | |||
126da8c499 | |||
ea110d5047 | |||
b37fad41c6 | |||
74775996db | |||
74426aab66 | |||
690bbb13d4 | |||
871fbed2b0 | |||
85c68d2ae6 | |||
8870611d4d | |||
67af7fc1bf | |||
93bea174e4 | |||
84fc6aabc8 | |||
38c4f5bcb6 | |||
e2af7e3149 | |||
950d75ba43 | |||
e651c99697 | |||
9694628d3f | |||
046582d1a0 | |||
f6f0ea9ae5 | |||
018547cdf3 | |||
d763a64f03 | |||
cefa460a0d | |||
cc5948ad0b | |||
be1943a66d | |||
d9acb47afe |
7
.cvsignore
Normal file
@ -0,0 +1,7 @@
|
||||
O.*
|
||||
*~
|
||||
bin
|
||||
lib
|
||||
dbd
|
||||
include
|
||||
*.pdf
|
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
O.*
|
||||
*~
|
||||
bin
|
||||
lib
|
||||
dbd
|
||||
include
|
||||
*.pdf
|
37
GNUmakefile
Normal file
@ -0,0 +1,37 @@
|
||||
ifeq ($(wildcard /ioc/tools/driver.makefile),)
|
||||
$(warning It seems you do not have the PSI build environment. Remove GNUmakefile.)
|
||||
include Makefile
|
||||
else
|
||||
include /ioc/tools/driver.makefile
|
||||
EXCLUDE_VERSIONS = 3.13.2
|
||||
PROJECT=stream
|
||||
BUILDCLASSES += Linux
|
||||
|
||||
DOCUDIR = documentation
|
||||
|
||||
PCRE=1
|
||||
ASYN=1
|
||||
-include src/CONFIG_STREAM
|
||||
|
||||
SOURCES += $(RECORDS:%=src/dev%Stream.c)
|
||||
SOURCES += $(FORMATS:%=src/%Converter.cc)
|
||||
SOURCES += $(BUSSES:%=src/%Interface.cc)
|
||||
SOURCES += $(STREAM_SRCS:%=src/%)
|
||||
|
||||
HEADERS += devStream.h
|
||||
HEADERS += StreamFormat.h
|
||||
HEADERS += StreamFormatConverter.h
|
||||
HEADERS += StreamBuffer.h
|
||||
HEADERS += StreamError.h
|
||||
|
||||
StreamCore.o: streamReferences
|
||||
|
||||
streamReferences:
|
||||
perl ../src/makeref.pl Interface $(BUSSES) > $@
|
||||
perl ../src/makeref.pl Converter $(FORMATS) >> $@
|
||||
|
||||
export DBDFILES = streamSup.dbd
|
||||
streamSup.dbd:
|
||||
@echo Creating $@
|
||||
perl ../src/makedbd.pl $(RECORDTYPES) > $@
|
||||
endif
|
5
MODULE
@ -1,5 +0,0 @@
|
||||
# Please change the following email with yours.
|
||||
Email: dirk.zimoch@psi.ch
|
||||
Module-Name: StreamDevice2
|
||||
Description: StreamDevice2
|
||||
Project-Name:
|
41
Makefile
@ -1,24 +1,29 @@
|
||||
TOP = ..
|
||||
|
||||
DIRS = src
|
||||
# Look if we have EPICS R3.13 or R3.14
|
||||
ifneq ($(wildcard $(TOP)/configure),)
|
||||
# EPICS R3.14
|
||||
DIRS = configure
|
||||
include $(TOP)/configure/CONFIG
|
||||
else ifneq ($(wildcard $(TOP)/config),)
|
||||
# EPICS R3.13
|
||||
CONFIG = $(TOP)/config
|
||||
DIRS = config
|
||||
include $(TOP)/config/CONFIG_APP
|
||||
else
|
||||
TOP = .
|
||||
DIRS = configure
|
||||
include $(TOP)/configure/CONFIG
|
||||
endif
|
||||
|
||||
DIRS += src
|
||||
src_DEPEND_DIRS = configure
|
||||
DIRS += streamApp
|
||||
streamApp_DEPEND_DIRS = src
|
||||
|
||||
# Look if we have EPICS R3.13 or R3.14
|
||||
ifeq ($(wildcard $(TOP)/configure),)
|
||||
# EPICS R3.13
|
||||
include $(TOP)/config/CONFIG_APP
|
||||
CONFIG = $(TOP)/config
|
||||
else
|
||||
# EPICS R3.14
|
||||
include $(TOP)/configure/CONFIG
|
||||
ifneq ($(words $(CALC) $(SYNAPPS)), 0)
|
||||
# with synApps calc module (contains scalcout)
|
||||
DIRS += srcSynApps
|
||||
srcSynApps_DEPEND_DIRS = src
|
||||
streamApp_DEPEND_DIRS += srcSynApps
|
||||
endif
|
||||
endif
|
||||
include $(CONFIG)/RULES_TOP
|
||||
|
||||
DIRS += streamApp
|
||||
documentation/stream.pdf: documentation/*.html documentation/*.css documentation/*.png
|
||||
cd documentation; makepdf
|
||||
|
||||
include $(CONFIG)/RULES_DIRS
|
||||
install: documentation/stream.pdf
|
||||
|
3
config/CONFIG
Normal file
@ -0,0 +1,3 @@
|
||||
#CONFIG
|
||||
# Add any changes to make rules here
|
||||
#CROSS_COMPILER_TARGET_ARCHS = mv167
|
20
config/CONFIG_APP
Normal file
@ -0,0 +1,20 @@
|
||||
#CONFIG_APP
|
||||
include $(TOP)/config/RELEASE
|
||||
-include $(TOP)/config/RELEASE.$(HOST_ARCH)
|
||||
include $(EPICS_BASE)/config/CONFIG
|
||||
INSTALL_LOCATION = $(TOP)
|
||||
ifdef INSTALL_LOCATION_APP
|
||||
INSTALL_LOCATION = $(INSTALL_LOCATION_APP)
|
||||
endif
|
||||
|
||||
ifdef T_A
|
||||
-include $(TOP)/config/O.$(T_A)/CONFIG_APP_INCLUDE
|
||||
endif
|
||||
|
||||
# Where to install databases
|
||||
INSTALL_DB = $(INSTALL_LOCATION)/db
|
||||
# dbst based database optimization (default: NO)
|
||||
DB_OPT = NO
|
||||
# May be overridden if not all databases should be installed
|
||||
INSTALLDB = $(DB)
|
||||
include $(TOP)/config/CONFIG
|
9
config/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
#
|
||||
# Makefile,v 1.1.2.1 1999/07/15 19:55:30 jba Exp
|
||||
#
|
||||
|
||||
TOP=..
|
||||
include $(TOP)/config/CONFIG_APP
|
||||
|
||||
include $(TOP)/config/RULES_ARCHS
|
||||
|
21
config/Makefile.Host
Normal file
@ -0,0 +1,21 @@
|
||||
#
|
||||
# Makefile.Host,v 1.1.2.3 2001/09/14 19:39:14 anj Exp
|
||||
#
|
||||
|
||||
TOP=../..
|
||||
include $(TOP)/config/CONFIG_APP
|
||||
|
||||
TARGETS = checkRelease CONFIG_APP_INCLUDE
|
||||
|
||||
include $(TOP)/config/RULES.Host
|
||||
|
||||
inc:: $(TARGETS)
|
||||
|
||||
CONFIG_APP_INCLUDE: $(wildcard $(TOP)/config/RELEASE*) $(TOP)/config/CONFIG_APP
|
||||
$(PERL) $(TOP)/config/convertRelease.pl $@
|
||||
|
||||
checkRelease:
|
||||
$(PERL) $(TOP)/config/convertRelease.pl $@
|
||||
|
||||
.PHONY :: checkRelease
|
||||
|
15
config/Makefile.Vx
Normal file
@ -0,0 +1,15 @@
|
||||
#
|
||||
# Makefile.Vx,v 1.1.2.3 2001/09/14 19:39:15 anj Exp
|
||||
#
|
||||
|
||||
TOP=../..
|
||||
include $(TOP)/config/CONFIG_APP
|
||||
|
||||
TARGETS = CONFIG_APP_INCLUDE
|
||||
|
||||
include $(TOP)/config/RULES.Vx
|
||||
|
||||
inc:: $(TARGETS)
|
||||
|
||||
CONFIG_APP_INCLUDE: $(wildcard $(TOP)/config/RELEASE*) $(TOP)/config/CONFIG_APP
|
||||
$(PERL) $(TOP)/config/convertRelease.pl -h $(HOST_ARCH) $@
|
1
config/RELEASE
Normal file
@ -0,0 +1 @@
|
||||
include $(TOP)/configure/RELEASE
|
203
config/RULES.Db
Normal file
@ -0,0 +1,203 @@
|
||||
# RULES.Db,v 1.9.6.6 2003/03/25 23:13:22 jba Exp
|
||||
#
|
||||
# Rules for making things related to databases
|
||||
#
|
||||
MAKEBPT = $(EPICS_BASE_HOST_BIN)/makeBpt$(EXE)
|
||||
DBLOADTEMPLATE = $(EPICS_BASE_HOST_BIN)/dbLoadTemplate$(EXE)
|
||||
DBEXPAND = $(EPICS_BASE_HOST_BIN)/dbExpand$(EXE)
|
||||
DBST = dbst
|
||||
MAKEDBDEPENDS = $(PERL) $(TOP)/config/makeDbDepends.pl
|
||||
REPLACEVAR = $(PERL) $(TOP)/config/replaceVAR.pl
|
||||
ifndef WIN32
|
||||
TOUCH = touch
|
||||
else
|
||||
TOUCH = $(PERL) $(TOP)/config/touch.pl
|
||||
endif
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
# if we are not building base add base dbd dirs
|
||||
|
||||
ifneq ($(EPICS_BASE),$(TOP))
|
||||
ifneq ($(EPICS_BASE),$(INSTALL_LOCATION))
|
||||
EPICS_DBDFLAGS += -I $(EPICS_BASE)/dbd
|
||||
endif
|
||||
endif
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# ----------------------------------------------------
|
||||
# create names (lists) for installed things
|
||||
# ----------------------------------------------------
|
||||
|
||||
INSTALL_BPTS = $(BPTS:%= $(INSTALL_DBD)/%)
|
||||
INSTALL_DBDS = $(DBDINSTALL:%= $(INSTALL_DBD)/%)
|
||||
INSTALL_DBDNAME = $(DBDNAME:%= $(INSTALL_DBD)/%)
|
||||
INSTALL_DATA = $(INSTALLDB:%=$(INSTALL_DB)/%)
|
||||
INSTALL_TEMPLATES = $(filter %.template,$(INSTALL_DATA))
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Main targets
|
||||
|
||||
all:: install
|
||||
|
||||
inc:: $(INSTALL_DBDS) $(INSTALL_BPTS) $(INSTALL_TEMPLATES)
|
||||
|
||||
rebuild:: clean install
|
||||
|
||||
install:: inc buildInstall
|
||||
|
||||
buildInstall:: build $(INSTALL_DATA)
|
||||
|
||||
depends::
|
||||
|
||||
clean::
|
||||
@echo "Cleaning"
|
||||
@$(RM) $(DB) $(DBDNAME) *.template *.substitutions *.db.raw \
|
||||
*.db-stamp *.edf esiread.cnf
|
||||
|
||||
##################################################### "Foreign" templates
|
||||
|
||||
TEMPLATE_LINKS = $(filter-out $(notdir $(USES_TEMPLATE)), $(USES_TEMPLATE))
|
||||
TEMPLATE_FILES = $(filter $(notdir $(USES_TEMPLATE)), $(USES_TEMPLATE))
|
||||
DB_STAMP = $(patsubst %.db, %.db-stamp, $(DB))
|
||||
DB_REALTARGET = $(patsubst %.db-stamp, %.db, $@)
|
||||
|
||||
ifneq '$(TEMPLATE_LINKS)' ''
|
||||
build:: $(notdir $(TEMPLATE_LINKS))
|
||||
endif
|
||||
build:: $(INSTALL_DBDNAME) $(TEMPLATE_FILES) $(DB_STAMP)
|
||||
|
||||
$(notdir $(TEMPLATE_LINKS)): %.template:
|
||||
ifndef WIN32
|
||||
@$(RM) $(notdir $(TEMPLATE_LINKS))
|
||||
ln -s $(TEMPLATE_LINKS) .
|
||||
# Workaround for dbLoadTemplate bug: terminate here if link target doesn't exist
|
||||
@cat $(TEMPLATE_LINKS) > /dev/null
|
||||
else
|
||||
@$(RM) $(notdir $(TEMPLATE_LINKS))
|
||||
$(CP) $(TEMPLATE_LINKS) .
|
||||
endif
|
||||
|
||||
##################################################### Inflated or plain databases
|
||||
|
||||
$(INSTALL_DB)/%.db: %.db-stamp
|
||||
@echo "Installing database $@"
|
||||
@$(INSTALL) -d -m 644 $(patsubst %.db-stamp, %.db, $<) $(@D)
|
||||
|
||||
# Must have DBDNAME defined to use dbst optimization
|
||||
ifndef DBDNAME
|
||||
DB_OPT = NO
|
||||
endif
|
||||
|
||||
# dbst based database optimization
|
||||
ifeq '$(DB_OPT)' 'YES'
|
||||
.PRECIOUS: %.db.raw
|
||||
%.db-stamp: %.db.raw $(INSTALL_DBD)/$(DBDNAME)
|
||||
@echo "Optimizing database $@"
|
||||
$(DBST) $(INSTALL_DBD)/$(DBDNAME) $< -d > $(DB_REALTARGET)
|
||||
@$(TOUCH) $@
|
||||
%.db-stamp: %.t.db.raw $(INSTALL_DBD)/$(DBDNAME)
|
||||
@echo "Optimizing database $@"
|
||||
$(DBST) $(INSTALL_DBD)/$(DBDNAME) $< -d > $(DB_REALTARGET)
|
||||
@$(TOUCH) $@
|
||||
else
|
||||
# NO optimization => move it and keep a stamp
|
||||
%.db-stamp: %.db.raw
|
||||
@$(MV) $< $(DB_REALTARGET)
|
||||
@$(TOUCH) $@
|
||||
@$(TOUCH) $<
|
||||
%.db-stamp: %.t.db.raw
|
||||
@$(MV) $< $(DB_REALTARGET)
|
||||
@$(TOUCH) $@
|
||||
@$(TOUCH) $<
|
||||
endif
|
||||
|
||||
%.t.db.raw: %.substitutions
|
||||
@echo "Inflating database from $<"
|
||||
@$(RM) $@
|
||||
@$(DBLOADTEMPLATE) $< > $@
|
||||
|
||||
##################################################### CapFast filter
|
||||
|
||||
%.edf:: ../%.sch $(DEPSCHS)
|
||||
@if [ ! -f cad.rc -a -r ../cad.rc ] ; then ln -s ../cad.rc ; fi
|
||||
$(SCH2EDIF) $(SCH2EDIF_SYSFLAGS) $(SCH2EDIF_FLAGS) $<
|
||||
|
||||
##################################################### Substitution files
|
||||
|
||||
$(INSTALL_DB)/%.substitutions: %.substitutions
|
||||
@echo "Installing $@"
|
||||
@$(INSTALL) -d -m 644 $(@F) $(@D)
|
||||
|
||||
%.substitutions:: ../%.substitutions
|
||||
@$(CP) $< $@
|
||||
|
||||
ifdef CREATESUBSTITUTIONS
|
||||
%.substitutions:: $(word $(words $(CREATESUBSTITUTIONS)),$(CREATESUBSTITUTIONS))
|
||||
@$(CREATESUBSTITUTIONS) $*
|
||||
endif
|
||||
|
||||
# Better make it PRECIOUS (to get around make bug)
|
||||
.PRECIOUS: %.substitutions
|
||||
|
||||
##################################################### Template databases
|
||||
|
||||
# Installed template files (dbLoadTemplate() on IOC side)
|
||||
$(INSTALL_DB)/%.template: %.template
|
||||
@echo "Installing $@"
|
||||
@$(INSTALL) -d -m 644 $(@F) $(@D)
|
||||
|
||||
%.template:: ../%.template
|
||||
@$(CP) $< $@
|
||||
|
||||
%.template: %.edf
|
||||
$(E2DB) $(E2DB_SYSFLAGS) $(E2DB_FLAGS) -n $@.VAR $<
|
||||
@$(REPLACEVAR) < $@.VAR > $@
|
||||
@$(RM) $@.VAR
|
||||
|
||||
##################################################### Flat databases
|
||||
|
||||
%.db.raw:: ../%.db
|
||||
$(CP) $< $@
|
||||
|
||||
%.db.raw: %.edf
|
||||
$(E2DB) $(E2DB_SYSFLAGS) $(E2DB_FLAGS) -n $@.VAR $<
|
||||
@$(REPLACEVAR) < $@.VAR > $@
|
||||
@$(RM) $@.VAR
|
||||
|
||||
##################################################### DBD stuff
|
||||
|
||||
$(INSTALL_DBD)/%: %
|
||||
@echo "Installing $@"
|
||||
@$(INSTALL) -d -m 644 $< $(@D)
|
||||
|
||||
$(INSTALL_DBD)/%:: ../%
|
||||
@echo "Installing $@"
|
||||
@$(INSTALL) -d -m 644 $< $(@D)
|
||||
|
||||
bpt%.dbd: bpt%.data
|
||||
$(RM) $@
|
||||
$(MAKEBPT) $<
|
||||
|
||||
bpt%.dbd:: ../bpt%.data
|
||||
$(RM) $@
|
||||
$(MAKEBPT) $<
|
||||
|
||||
# Patch for old applications
|
||||
ifdef USER_DBDFLAGS
|
||||
DBDFLAGS = $(USER_DBDFLAGS)
|
||||
endif
|
||||
|
||||
ifdef DBDEXPAND
|
||||
$(DBDNAME): ../$(DBDEXPAND)
|
||||
@echo "Expanding dbd"
|
||||
@$(RM) $@
|
||||
$(DBEXPAND) $(DBDFLAGS) $< > dbExpand.tmp
|
||||
$(MV) dbExpand.tmp $@
|
||||
endif
|
||||
|
||||
##################################################### Dependencies
|
||||
|
||||
DEPENDS: $(filter $(patsubst %.db, %.substitutions, $(DB)), $(wildcard *.substitutions))
|
||||
@$(MAKEDBDEPENDS) $^
|
||||
|
||||
-include DEPENDS
|
3
config/RULES.Host
Normal file
@ -0,0 +1,3 @@
|
||||
#RULES.Host
|
||||
|
||||
include $(EPICS_BASE)/config/RULES.Host
|
7
config/RULES.Vx
Normal file
@ -0,0 +1,7 @@
|
||||
#RULES.Vx
|
||||
include $(EPICS_BASE)/config/RULES.Vx
|
||||
inc:: $(INSTALL_INCREC)
|
||||
|
||||
# Library should be rebuilt because LIBOBJS may have changed.
|
||||
$(LIBNAME): ../Makefile.Vx
|
||||
|
9
config/RULES.ioc
Normal file
@ -0,0 +1,9 @@
|
||||
#RULES.ioc
|
||||
include $(EPICS_BASE)/config/RULES_DIRS
|
||||
buildInstall:: cdCommands
|
||||
|
||||
cdCommands: Makefile $(wildcard $(TOP)/config/RELEASE*)
|
||||
@$(PERL) $(TOP)/config/convertRelease.pl -a $(ARCH) -h $(HOST_ARCH) $@
|
||||
|
||||
clean::
|
||||
@$(RM) cdCommands
|
4
config/RULES.iocBoot
Normal file
@ -0,0 +1,4 @@
|
||||
#RULES.iocBoot
|
||||
DIRS += $(wildcard ioc*)
|
||||
DIRS += $(wildcard as*)
|
||||
include $(EPICS_BASE)/config/RULES_DIRS
|
14
config/RULES.munch
Normal file
@ -0,0 +1,14 @@
|
||||
MUNCH = $(PERL) $(INSTALL_LOCATION)/bin/$(HOST_ARCH)/munch.pl
|
||||
|
||||
# The original 3.13.10 munching rule does not really work well
|
||||
|
||||
build:: $(LIBNAME).munch
|
||||
|
||||
buildInstall:: $(INSTALL_BIN)/$(LIBNAME).munch
|
||||
|
||||
%.munch: %
|
||||
$(RM) $*_ctct.o $*_ctdt.c
|
||||
$(NM) $< | $(MUNCH) > $*_ctdt.c
|
||||
$(GCC) -traditional $(CFLAGS) -fdollars-in-identifiers -c $(SOURCE_FLAG) $*_ctdt.c
|
||||
$(LINK.c) $@ $< $*_ctdt.o
|
||||
|
2
config/RULES_ARCHS
Normal file
@ -0,0 +1,2 @@
|
||||
#RULES_ARCHS
|
||||
include $(EPICS_BASE)/config/RULES_ARCHS
|
2
config/RULES_DIRS
Normal file
@ -0,0 +1,2 @@
|
||||
#RULES_DIRS
|
||||
include $(EPICS_BASE)/config/RULES_DIRS
|
5
config/RULES_TOP
Normal file
@ -0,0 +1,5 @@
|
||||
#RULES_TOP
|
||||
include $(EPICS_BASE)/config/RULES_TOP
|
||||
|
||||
uninstall::
|
||||
@$(RMDIR) $(INSTALL_DB)
|
186
config/convertRelease.pl
Normal file
@ -0,0 +1,186 @@
|
||||
eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*-
|
||||
if $running_under_some_shell; # convertRelease.pl
|
||||
#
|
||||
# convertRelease.pl,v 1.1.2.1 2001/09/14 19:39:15 anj Exp
|
||||
#
|
||||
# Parse config/RELEASE file(s) and generate a derived output file.
|
||||
#
|
||||
# This tool replaces makeConfigAppInclude.pl and makeIocCdCommands.pl
|
||||
# and adds consistency checks for RELEASE files.
|
||||
#
|
||||
|
||||
use Cwd;
|
||||
use Getopt::Std;
|
||||
|
||||
$cwd = cwd();
|
||||
$cwd =~ s/\/tmp_mnt//; # hack for sun4
|
||||
$cwd =~ s/\\/\//g; # hack for win32
|
||||
|
||||
getopt "aht";
|
||||
|
||||
if ($opt_a) {
|
||||
$arch = $opt_a;
|
||||
} else { # Look for O.<arch> in current path
|
||||
$_ = $cwd;
|
||||
($arch) = /.*\/O.([\w-]+)$/;
|
||||
}
|
||||
|
||||
$hostarch = $arch;
|
||||
$hostarch = $opt_h if ($opt_h);
|
||||
|
||||
if ($opt_t) {
|
||||
$top = $opt_t;
|
||||
} else { # Find $top from current path
|
||||
$top = $cwd;
|
||||
$top =~ s/\/iocBoot.*//;
|
||||
$top =~ s/\/config\/O\..*//;
|
||||
}
|
||||
|
||||
unless (@ARGV == 1) {
|
||||
print "Usage: convertRelease.pl [-a arch] [-h hostarch] [-t top] outfile\n";
|
||||
print " where outfile is be one of:\n";
|
||||
print "\tcheckRelease - checks consistency with support apps\n";
|
||||
print "\tcdCommands - generate cd path strings for IOC use\n";
|
||||
print "\tCONFIG_APP_INCLUDE - additional build variables\n";
|
||||
exit 2;
|
||||
}
|
||||
$outfile = $ARGV[0];
|
||||
|
||||
# TOP refers to this application
|
||||
%macros = (TOP => $top);
|
||||
@apps = (TOP); # Provides the order of apps in RELEASE file
|
||||
|
||||
# Read the RELEASE file(s)
|
||||
$relfile = "$top/config/RELEASE";
|
||||
die "Can't find config/RELEASE file" unless (-r $relfile);
|
||||
&readRelease($relfile, \%macros, \@apps);
|
||||
|
||||
if ($hostarch) {
|
||||
$relfile .= ".$hostarch";
|
||||
&readRelease($relfile, \%macros, \@apps) if (-r $relfile);
|
||||
}
|
||||
|
||||
# This is a perl switch statement:
|
||||
for ($outfile) {
|
||||
/CONFIG_APP_INCLUDE/ and do { &configAppInclude; last; };
|
||||
/cdCommands/ and do { &cdCommands; last; };
|
||||
/checkRelease/ and do { &checkRelease; last; };
|
||||
die "Output file type \'$outfile\' not supported";
|
||||
}
|
||||
|
||||
sub readRelease {
|
||||
my ($file, $Rmacros, $Rapps) = @_;
|
||||
# $Rmacros is a reference to a hash, $Rapps a ref to an array
|
||||
my ($pre, $macro, $post, $path);
|
||||
local *IN;
|
||||
open(IN, $file) or die "Can't open $file: $!\n";
|
||||
while (<IN>) {
|
||||
chomp;
|
||||
s/\s*#.*$//; # Remove trailing comments
|
||||
next if /^\s*$/; # Skip blank lines
|
||||
|
||||
# Expand all macros in the line:
|
||||
while (($pre,$macro,$post) = /(.*)\$\((\w+)\)(.*)/, $macro ne "") {
|
||||
$_ = $pre . $Rmacros->{$macro} . $post;
|
||||
}
|
||||
|
||||
# Handle "<macro> = <path>"
|
||||
($macro, $path) = /^\s*(\w+)\s*=\s*(.*)/;
|
||||
if ($macro ne "") {
|
||||
$Rmacros->{$macro} = $path;
|
||||
push @$Rapps, $macro;
|
||||
next;
|
||||
}
|
||||
# Handle "include <path>" syntax
|
||||
($path) = /^\s*include\s+(.*)/;
|
||||
&readRelease($path, $Rmacros, $Rapps) if (-r $path);
|
||||
}
|
||||
close IN;
|
||||
}
|
||||
|
||||
sub configAppInclude {
|
||||
@includes = grep !/^(TOP|TEMPLATE_TOP)$/, @apps;
|
||||
|
||||
unlink($outfile);
|
||||
open(OUT,">$outfile") or die "$! creating $outfile";
|
||||
print OUT "# Do not modify this file, changes made here will\n";
|
||||
print OUT "# be lost when the application is next rebuilt.\n\n";
|
||||
|
||||
if ($arch) {
|
||||
foreach $app (@includes) {
|
||||
$path = $macros{$app};
|
||||
next unless (-d "$path/bin/$arch");
|
||||
print OUT "${app}_BIN = \$($app)/bin/$arch\n";
|
||||
}
|
||||
foreach $app (@includes) {
|
||||
$path = $macros{$app};
|
||||
next unless (-d "$path/lib/$arch");
|
||||
print OUT "${app}_LIB = \$($app)/lib/$arch\n";
|
||||
}
|
||||
}
|
||||
foreach $app (@includes) {
|
||||
$path = $macros{$app};
|
||||
next unless (-d "$path/include");
|
||||
print OUT "EPICS_INCLUDES += -I\$($app)/include\n";
|
||||
}
|
||||
foreach $app (@includes) {
|
||||
$path = $macros{$app};
|
||||
next unless (-d "$path/dbd");
|
||||
print OUT "EPICS_DBDFLAGS += -I \$($app)/dbd\n";
|
||||
}
|
||||
close OUT;
|
||||
}
|
||||
|
||||
sub cdCommands {
|
||||
die "Architecture not set (use -a option)" unless ($arch);
|
||||
@includes = grep !/^TEMPLATE_TOP$/, @apps;
|
||||
|
||||
# if -t <top> was given, substitute it in the startup path
|
||||
$startup = $cwd;
|
||||
$startup =~ s/.*(\/iocBoot\/.*)/$top$1/ if ($opt_t);
|
||||
|
||||
unlink($outfile);
|
||||
open(OUT,">$outfile") or die "$! creating $outfile";
|
||||
print OUT "startup = \"$startup\"\n";
|
||||
print OUT "appbin = \"$top/bin/$arch\"\n"; # compatibility with R3.13.1
|
||||
|
||||
foreach $app (@includes) {
|
||||
$path = $macros{$app};
|
||||
$lcapp = lc($app);
|
||||
print OUT "$lcapp = \"$path\"\n" if (-d $path);
|
||||
print OUT "${lcapp}bin = \"$path/bin/$arch\"\n" if (-d "$path/bin/$arch");
|
||||
}
|
||||
close OUT;
|
||||
}
|
||||
|
||||
sub checkRelease {
|
||||
$status = 0;
|
||||
delete $macros{TOP};
|
||||
delete $macros{TEMPLATE_TOP};
|
||||
|
||||
while (($app, $path) = each %macros) {
|
||||
%check = (TOP => $path);
|
||||
@order = ();
|
||||
$relfile = "$path/config/RELEASE";
|
||||
&readRelease($relfile, \%check, \@order) if (-r $relfile);
|
||||
if ($hostarch) {
|
||||
$relfile .= ".$hostarch";
|
||||
&readRelease($relfile, \%check, \@order) if (-r $relfile);
|
||||
}
|
||||
delete $check{TOP};
|
||||
|
||||
while (($parent, $ppath) = each %check) {
|
||||
if (exists $macros{$parent} && ($macros{$parent} ne $ppath)) {
|
||||
print "\n" unless ($status);
|
||||
print "Definition of $parent conflicts with $app support.\n";
|
||||
print "In this application config/RELEASE defines\n";
|
||||
print "\t$parent = $macros{$parent}\n";
|
||||
print "but $app at $path has\n";
|
||||
print "\t$parent = $ppath\n";
|
||||
$status = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
print "\n" if ($status);
|
||||
exit $status;
|
||||
}
|
23
config/makeDbDepends.pl
Normal file
@ -0,0 +1,23 @@
|
||||
eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*-
|
||||
if $running_under_some_shell; # makeDbDepends.pl
|
||||
|
||||
# Called from within the object directory.
|
||||
# Searches the .substitutions files (from the command line) for
|
||||
# "file xxx {" entries to create a .DEPENDS file
|
||||
|
||||
open(OUT, ">.DEPENDS") or die "Cannot open .DEPENDS: $!";
|
||||
|
||||
foreach $file (@ARGV) {
|
||||
open(IN, "<$file") or die "Cannot open $file: $!";
|
||||
@substfile = <IN>;
|
||||
close IN or die "Cannot close $file: $!";
|
||||
|
||||
@depends = grep { s/^\s*file\s*(.*)\s*\{.*$/\1/ } @substfile;
|
||||
chomp @depends;
|
||||
|
||||
if (@depends) {
|
||||
$file =~ s/\.substitutions/\.t.db.raw/;
|
||||
print OUT "${file}:: @depends\n";
|
||||
}
|
||||
}
|
||||
close OUT or die "Cannot close $file: $!";
|
12
config/replaceVAR.pl
Normal file
@ -0,0 +1,12 @@
|
||||
eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*-
|
||||
if $running_under_some_shell; # replaceVAR.pl
|
||||
|
||||
# Called from within the object directory
|
||||
# Replaces VAR(xxx) with $(xxx)
|
||||
# and VAR_xxx_ with $(xxx)
|
||||
|
||||
while (<STDIN>) {
|
||||
s/VAR\(/\$\(/g;
|
||||
s/VAR_([^_]*)_/\$\($1\)/g;
|
||||
print;
|
||||
}
|
31
config/touch.pl
Normal file
@ -0,0 +1,31 @@
|
||||
#!/usr/bin/perl
|
||||
#
|
||||
# unix touch in Perl
|
||||
|
||||
use File::Copy;
|
||||
use File::Basename;
|
||||
|
||||
sub Usage
|
||||
{
|
||||
my ($txt) = @_;
|
||||
|
||||
print "Usage:\n";
|
||||
print "\ttouch file [ file2 file3 ...]\n";
|
||||
print "\nError: $txt\n" if $txt;
|
||||
|
||||
exit 2;
|
||||
}
|
||||
|
||||
# need at least one arg: ARGV[0]
|
||||
Usage("need more args") if $#ARGV < 0;
|
||||
|
||||
@targets=@ARGV[0..$#ARGV];
|
||||
|
||||
foreach $file ( @targets )
|
||||
{
|
||||
open(OUT,">$file") or die "$! creating $file";
|
||||
#print OUT "NUL\n";
|
||||
close OUT;
|
||||
}
|
||||
|
||||
# EOF touch.pl
|
14
configure/CONFIG
Normal file
@ -0,0 +1,14 @@
|
||||
#CONFIG
|
||||
include $(TOP)/configure/CONFIG_APP
|
||||
# Add any changes to make definitions here
|
||||
|
||||
#CROSS_COMPILER_TARGET_ARCHS = vxWorks-68040
|
||||
#CROSS_COMPILER_TARGET_ARCHS =
|
||||
|
||||
# Use this when your IOC and the host use different paths
|
||||
# to access the application. Typically this will be
|
||||
# used with the Microsoft FTP server or with NFS mounts. Use
|
||||
# is indicated by failure of the cdCommands script on
|
||||
# vxWorks. You must rebuild in the iocBoot directory
|
||||
# before this takes effect.
|
||||
#IOCS_APPL_TOP = <the top of the application as seen by the IOC>
|
27
configure/CONFIG_APP
Normal file
@ -0,0 +1,27 @@
|
||||
# CONFIG_APP
|
||||
|
||||
include $(TOP)/configure/RELEASE
|
||||
-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH)
|
||||
-include $(TOP)/configure/RELEASE.Common.$(T_A)
|
||||
-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A)
|
||||
|
||||
ifneq ($(wildcard $(EPICS_BASE)/configure),)
|
||||
CONFIG=$(EPICS_BASE)/configure
|
||||
else
|
||||
CONFIG=$(EPICS_BASE)/config
|
||||
DIRS += config
|
||||
endif
|
||||
include $(CONFIG)/CONFIG
|
||||
|
||||
INSTALL_LOCATION = $(TOP)
|
||||
ifdef INSTALL_LOCATION_APP
|
||||
INSTALL_LOCATION = $(INSTALL_LOCATION_APP)
|
||||
endif
|
||||
|
||||
ifdef T_A
|
||||
-include $(TOP)/configure/O.$(T_A)/CONFIG_APP_INCLUDE
|
||||
endif
|
||||
|
||||
# dbst based database optimization (default: NO)
|
||||
DB_OPT = NO
|
||||
HOST_OPT=NO
|
15
configure/Makefile
Normal file
@ -0,0 +1,15 @@
|
||||
# Makefile
|
||||
|
||||
TOP=..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
# Set the following to NO to disable consistency checking of
|
||||
# the support applications defined in $(TOP)/configure/RELEASE
|
||||
CHECK_RELEASE = YES
|
||||
|
||||
TARGETS = $(CONFIG_TARGETS)
|
||||
CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS)))
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
29
configure/RELEASE
Normal file
@ -0,0 +1,29 @@
|
||||
#RELEASE Location of external products
|
||||
# Run "gnumake clean uninstall install" in the application
|
||||
# top directory each time this file is changed.
|
||||
#
|
||||
# NOTE: The build does not check dependancies on files
|
||||
# external to this application. Thus you should run
|
||||
# "gnumake clean uninstall install" in the top directory
|
||||
# each time EPICS_BASE, SNCSEQ, or any other external
|
||||
# module defined in the RELEASE file is rebuilt.
|
||||
|
||||
TEMPLATE_TOP=$(EPICS_BASE)/templates/makeBaseApp/top
|
||||
|
||||
# If you don't want to install into $(TOP) then
|
||||
# define INSTALL_LOCATION_APP here
|
||||
#INSTALL_LOCATION_APP=<fullpathname>
|
||||
|
||||
EPICS_BASE=/usr/local/epics/base-3.14.12
|
||||
ASYN=~/top/asyn4-30
|
||||
CALC=~/top/SynApps/calc-2-8
|
||||
PCRE=~/top/pcre-7.2
|
||||
|
||||
#3.14.8 does not understand ~
|
||||
#EPICS_BASE=/usr/local/epics/base-3.14.8
|
||||
#ASYN=/afs/psi.ch/user/z/zimoch/top/asyn-4.10
|
||||
|
||||
##Example 3.13 build
|
||||
#EPICS_BASE=/usr/local/epics/base-3.13.10
|
||||
#ASYN=/afs/psi.ch/user/z/zimoch/top_3.13/4-6
|
||||
#COMPAT=/afs/psi.ch/user/z/zimoch/top_3.13
|
9
configure/RULES
Normal file
@ -0,0 +1,9 @@
|
||||
#CONFIG
|
||||
ifneq ($(wildcard $(EPICS_BASE)/configure),)
|
||||
include $(EPICS_BASE)/configure/RULES
|
||||
else
|
||||
include $(EPICS_BASE)/config/RULES_ARCHS
|
||||
endif
|
||||
|
||||
# Library should be rebuilt because LIBOBJS may have changed.
|
||||
$(LIBNAME): ../Makefile
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
@ -41,6 +41,12 @@ written or read value.
|
||||
(=20.0/0xFFFF) maps 0x0000 to -10.0, 0x7FFF to 0.0 and 0xFFFF to 10.0.
|
||||
Using unsigned formats with values ≥ 0x800000 gives different results
|
||||
on 64 bit machines.
|
||||
<p>
|
||||
If <code>LINR=="NO CONVERSION"</code> (the default), <code>VAL</code>
|
||||
is directly converted from and to <code>long</code> without going through
|
||||
<code>RVAL</code>. This allows for more bits on 64 bit machines.
|
||||
To get the old behavior, use <code>LINR=="LINEAR"</code>.
|
||||
</p>
|
||||
</dd>
|
||||
<dt>ENUM format (e.g. <code>%{</code>):</dt>
|
||||
<dd>
|
||||
@ -78,6 +84,6 @@ written or read value.
|
||||
<a href="calcout.html">calcout</a>
|
||||
<a href="scalcout.html">scalcout</a>
|
||||
</p>
|
||||
<p><small>Dirk Zimoch, 2005</small></p>
|
||||
<p><small>Dirk Zimoch, 2015</small></p>
|
||||
</body>
|
||||
</html>
|
@ -40,6 +40,12 @@ written or read value.
|
||||
(=20.0/0xFFFF) maps -10.0 to 0x0000, 0.0 to 0x7FFF and 10.0 to 0xFFFF.
|
||||
Using unsigned formats with values ≥ 0x800000 gives different results
|
||||
on 64 bit machines.
|
||||
<p>
|
||||
If <code>LINR=="NO CONVERSION"</code> (the default), <code>OVAL</code>
|
||||
is directly converted to <code>long</code> without going through
|
||||
<code>RVAL</code>. This allows for more bits on 64 bit machines.
|
||||
To get the old behavior, use <code>LINR=="LINEAR"</code>.
|
||||
</p>
|
||||
</dd>
|
||||
<dt>ENUM format (e.g. <code>%{</code>):</dt>
|
||||
<dd>
|
||||
@ -79,6 +85,6 @@ written or read value.
|
||||
<a href="calcout.html">calcout</a>
|
||||
<a href="scalcout.html">scalcout</a>
|
||||
</p>
|
||||
<p><small>Dirk Zimoch, 2005</small></p>
|
||||
<p><small>Dirk Zimoch, 2015</small></p>
|
||||
</body>
|
||||
</html>
|
Before Width: | Height: | Size: 187 B After Width: | Height: | Size: 187 B |
Before Width: | Height: | Size: 187 B After Width: | Height: | Size: 187 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@ -28,8 +28,8 @@ A format converter consists of
|
||||
</p>
|
||||
<ul>
|
||||
<li>The <code>%</code> character</li>
|
||||
<li>Optionally a field <span class="new">or record</span> name in <code>()</code></li>
|
||||
<li>Optionally flags out of the characters <code>*# +0-<span class="new">?=</span></code></li>
|
||||
<li>Optionally a field or record name in <code>()</code></li>
|
||||
<li>Optionally flags out of the characters <code>*# +0-?=!</code></li>
|
||||
<li>Optionally an integer <em>width</em> field</li>
|
||||
<li>Optionally a period character (<code>.</code>) followed
|
||||
by an integer <em>precision</em> field (input ony for most formats)</li>
|
||||
@ -40,7 +40,7 @@ A format converter consists of
|
||||
<p>
|
||||
The flags <code>*# +0-</code> work like in the C functions
|
||||
<em>printf()</em> and <em>scanf()</em>.
|
||||
The flags <code>?</code> and <code>=</code> are extensions.
|
||||
The flags <code>?</code>, <code>=</code> and <code>!</code> are extensions.
|
||||
</p>
|
||||
<p>
|
||||
The <code>*</code> flag skips data in input formats.
|
||||
@ -67,16 +67,24 @@ The <code>0</code> flag says that numbers should be left padded with
|
||||
The <code>-</code> flag specifies that output is left justified if
|
||||
<em>width</em> is larger than required.
|
||||
</p>
|
||||
<p class="new">
|
||||
<p>
|
||||
The <code>?</code> flag makes failing input conversions succeed with
|
||||
a default zero value (0, 0.0, or "", depending on the format type).
|
||||
</p>
|
||||
<p class="new">
|
||||
<p>
|
||||
The <code>=</code> flag allows to compare input with current values.
|
||||
It is only allowed in input formats.
|
||||
Instead of reading a new value from input, the current value is
|
||||
formatted (like for output) and then compared to the input.
|
||||
</p>
|
||||
<p>
|
||||
The <code>!</code> flag demands that input is exactly <em>width</em>
|
||||
bytes long (normally <em>width</em> defines the maximum number of
|
||||
bytes read in many formats).
|
||||
For example <code>in "%!5d";</code> expects exactly 5 digits.
|
||||
Fewer digits are considered loss of data and make the format fail.
|
||||
This feature has been added by Klemen Vodopivec, SNS.
|
||||
</p>
|
||||
|
||||
<h3>Examples:</h3>
|
||||
<table>
|
||||
@ -143,11 +151,8 @@ field formatted as a string.
|
||||
Use <code>in "%(<i>otherrecord</i>.RVAL)f";</code> to write the floating
|
||||
point input value into the <code>RVAL</code> field of
|
||||
<code><i>otherrecord</i></code>.
|
||||
<span class="new">
|
||||
If no field is given for an other record .VAL is assumed.
|
||||
When a record name conflicts with a field name use .VAL explicitly.
|
||||
</span>
|
||||
|
||||
</p>
|
||||
<p>
|
||||
This feature is very useful when one line of input contains many values that should
|
||||
@ -158,6 +163,9 @@ attribute (see
|
||||
target="ex">Record Reference Manual</a>), the record will be processed.
|
||||
It is your responsibility that the data type of the record field is
|
||||
compatible to the the data type of the converter.
|
||||
STRING formats are compatible with arrays of CHAR or UCHAR.
|
||||
</p>
|
||||
<p>
|
||||
Note that using this syntax is by far not as efficient as using the
|
||||
default field.
|
||||
At the moment it is not possible to set <code>otherrecord</code> to an alarm
|
||||
@ -189,13 +197,13 @@ With the <code>#</code> flag, output always contains a period character.
|
||||
<p>
|
||||
<b>Input:</b> All these formats are equivalent. Leading whitespaces are skipped.
|
||||
</p>
|
||||
<p class="new">
|
||||
<p>
|
||||
With the <code>#</code> flag additional whitespace between sign and number
|
||||
is accepted.
|
||||
</p>
|
||||
<p class="new">
|
||||
<p>
|
||||
When a maximum field width is given, leading whitespace only counts to the
|
||||
field witdth when the space flag is given.
|
||||
field witdth when the space flag is used.
|
||||
</p>
|
||||
|
||||
<a name="stdl"></a>
|
||||
@ -210,6 +218,10 @@ field witdth when the space flag is given.
|
||||
<p>
|
||||
With the <code>#</code> flag, octal values are prefixed with <code>0</code>
|
||||
and hexadecimal values with <code>0x</code> or <code>0X</code>.
|
||||
<p>
|
||||
Unlike printf, <code>%x</code> and <code>%X</code> truncate the
|
||||
output to the the given width (number of least significant half bytes).
|
||||
</p>
|
||||
</p>
|
||||
<p>
|
||||
<b>Input:</b> <code>%d</code> matches signed decimal, <code>%u</code> matches
|
||||
@ -221,16 +233,16 @@ Octal and hexadecimal values can optionally be prefixed.
|
||||
hexadecimal notation.
|
||||
Leading whitespaces are skipped.
|
||||
</p>
|
||||
<p class="new">
|
||||
<p>
|
||||
With the <code>-</code> negative octal and hexadecimal values are accepted.
|
||||
</p>
|
||||
<p class="new">
|
||||
<p>
|
||||
With the <code>#</code> flag additional whitespace between sign and number
|
||||
is accepted.
|
||||
</p>
|
||||
<p class="new">
|
||||
<p>
|
||||
When a maximum field width is given, leading whitespace only counts to the
|
||||
field witdth when the space flag is given.
|
||||
field witdth when the space flag is used.
|
||||
</p>
|
||||
|
||||
<a name="stds"></a>
|
||||
@ -246,16 +258,19 @@ and <code>%c</code> matches a sequence of not-null characters.
|
||||
The maximum string length is given by <em>width</em>.
|
||||
The default <em>width</em> is infinite for <code>%s</code> and
|
||||
1 for <code>%c</code>.
|
||||
Leading whitespaces are skipped with <code>%s</code>
|
||||
<span class="new">
|
||||
except when the space flag is given</span>
|
||||
but not with <code>%c</code>.
|
||||
Leading whitespaces are skipped with <code>%s</code> except when
|
||||
the space flag is used but not with <code>%c</code>.
|
||||
The empty string matches.
|
||||
</p>
|
||||
<p class="new">
|
||||
<p>
|
||||
With the <code>#</code> flag <code>%s</code> matches a sequence of not-null
|
||||
characters instead of non-whitespace characters.
|
||||
</p>
|
||||
<p>
|
||||
With the <code>0</code> flag <code>%s</code> pads with 0 bytes instead of
|
||||
spaces.
|
||||
</p>
|
||||
|
||||
|
||||
<a name="cset"></a>
|
||||
<h2>6. Standard Charset STRING Converter (<code>%[<em>charset</em>]</code>)</h2>
|
||||
@ -286,9 +301,20 @@ to the value 0, <code>STANDBY</code> to 1 and <code>ON</code> to 2.
|
||||
<p>
|
||||
When using the <code>#</code> flag it is allowed to assign integer values
|
||||
to the strings using <code>=</code>.
|
||||
Unassigned strings increment their values by 1 as usual.
|
||||
</p>
|
||||
<p>
|
||||
Example: <code>%#{neg=-1|stop=0|pos=1|fast=10}</code>.
|
||||
If one string is the initial substing of another, the substing must come
|
||||
later to ensure correct matching.
|
||||
In particular if one string is the emptry string, it must be the last one.
|
||||
Use <code>#</code> and <code>=</code> to renumber if necessary.
|
||||
</p>
|
||||
<p>
|
||||
Use the assignment <code>=?</code> for the last string to make it the
|
||||
default value for output formats.
|
||||
</p>
|
||||
<p>
|
||||
Example: <code>%#{neg=-1|stop|pos|fast=10|rewind=-10}</code>.
|
||||
</p>
|
||||
<p>
|
||||
If one of the strings contains <code>|</code> or <code>}</code>
|
||||
@ -296,10 +322,11 @@ If one of the strings contains <code>|</code> or <code>}</code>
|
||||
a <code>\</code> must be used to escape the character.
|
||||
</p>
|
||||
<p>
|
||||
In output, depending on the value, one of the strings is printed.
|
||||
<b>Output:</b> Depending on the value, one of the strings is printed,
|
||||
or the default if no value matches.
|
||||
</p>
|
||||
<p>
|
||||
In input, if any of the strings matches the value is set accordingly.
|
||||
<b>Input:</b> If any of the strings matches, the value is set accordingly.
|
||||
</p>
|
||||
|
||||
<a name="bin"></a>
|
||||
@ -345,9 +372,12 @@ endian</em>, i.e. least significant byte first.
|
||||
With the <code>0</code> flag, the value is unsigned, otherwise signed.
|
||||
</p>
|
||||
<p>
|
||||
In output, the <em>prec</em> (or sizeof(long) whatever is less) least
|
||||
In output, the <em>precision</em> (or sizeof(long) whatever is less) least
|
||||
significant bytes of the value are sign extended or zero extended
|
||||
(depending on the <code>0</code> flag) to <em>width</em> bytes.
|
||||
The default for <em>precision</em> is 1. Thus if you do not specify
|
||||
the <em>precision</em>, only the least significant byte is written!
|
||||
It is common error to write <code>out "%2r";</code> instead of <code>out "%.2r";</code>.
|
||||
</p>
|
||||
<p>
|
||||
In input, <em>width</em> bytes are read and put into the value.
|
||||
@ -358,7 +388,7 @@ the value is sign extended or zero extended, depending on the
|
||||
<code>0</code> flag.
|
||||
</p>
|
||||
<p>
|
||||
Example: <code>out "%.2r"</code>
|
||||
Examples: <code>out "%.2r"; in "%02r";</code>
|
||||
</p>
|
||||
|
||||
<a name="rawdouble"></a>
|
||||
@ -412,7 +442,7 @@ The <em>width</em> field is the byte number from which to start
|
||||
calculating the checksum.
|
||||
Default is 0, i.e. the first byte of the input or output of the current
|
||||
command.
|
||||
The last byte is <em>prec</em> bytes before the checksum (default 0).
|
||||
The last byte is <em>precision</em> bytes before the checksum (default 0).
|
||||
For example in <code>"abcdefg%<xor>"</code> the checksum is calculated
|
||||
from <code>abcdefg</code>,
|
||||
but in <code>"abcdefg%2.1<xor>"</code> only from <code>cdef</code>.
|
||||
@ -424,8 +454,17 @@ With the <code>#</code> flag, the byte order is changed to <em>little
|
||||
endian</em>, i.e. least significant byte first.
|
||||
</p>
|
||||
<p>
|
||||
The <code>0</code> flag changes the checksum representation from
|
||||
binary to hexadecimal ASCII (2 bytes per checksum byte).
|
||||
The <code>0</code> flag changes the checksum representation to
|
||||
to hexadecimal ASCII (2 chars per checksum byte).
|
||||
</p>
|
||||
<p>
|
||||
The <code>-</code> flag changes the checksum representation to
|
||||
"poor man's hex": 0x30 ... 0x3f (2 chars per checksum byte).
|
||||
</p>
|
||||
<p>
|
||||
The <code>+</code> flag changes the checksum representation to
|
||||
decimal ASCII (formatted with %d).
|
||||
</p>
|
||||
<!--
|
||||
In output, the case of the ASCII checksum matches the case of first
|
||||
letter of the function name.
|
||||
@ -466,21 +505,24 @@ In input, the next byte or bytes must match the checksum.
|
||||
(poly=0x07, init=0x00, xorout=0x00).</dd>
|
||||
<dt><code>%<ccitt8></code></dt>
|
||||
<dd>One byte. The CCITT standard 8 bit crc checksum
|
||||
(poly=0x31, init=0x00, xorout=0x00).</dd>
|
||||
(poly=0x31, init=0x00, xorout=0x00, reflected).</dd>
|
||||
<dt><code>%<crc16></code></dt>
|
||||
<dd>Two bytes. An often used 16 bit crc checksum
|
||||
(poly=0x8005, init=0x0000, xorout=0x0000).</dd>
|
||||
<dt><code>%<crc16r></code></dt>
|
||||
<dd>Two bytes. An often used reflected 16 bit crc checksum
|
||||
(poly=0x8005, init=0x0000, xorout=0x0000).</dd>
|
||||
(poly=0x8005, init=0x0000, xorout=0x0000, reflected).</dd>
|
||||
<dt><code>%<modbus></code></dt>
|
||||
<dd>Two bytes. The modbus 16 bit crc checksum
|
||||
(poly=0x8005, init=0xffff, xorout=0x0000, reflected)</dd>
|
||||
<dt><code>%<ccitt16></code></dt>
|
||||
<dd>Two bytes. The usual (but <a target="ex"
|
||||
href="http://www.joegeluso.com/software/articles/ccitt.htm">wrong?</a>)
|
||||
href="http://srecord.sourceforge.net/crc16-ccitt.html">wrong?</a>)
|
||||
implementation of the CCITT standard 16 bit crc checksum
|
||||
(poly=0x1021, init=0xFFFF, xorout=0x0000).</dd>
|
||||
<dt><code>%<ccitt16a></code></dt>
|
||||
<dd>Two bytes. The unusual (but <a target="ex"
|
||||
href="http://www.joegeluso.com/software/articles/ccitt.htm">correct?</a>)
|
||||
href="http://srecord.sourceforge.net/crc16-ccitt.html">correct?</a>)
|
||||
implementation of the CCITT standard 16 bit crc checksum with augment.
|
||||
(poly=0x1021, init=0x1D0F, xorout=0x0000).</dd>
|
||||
<dt><code>%<ccitt16x></code> or <code>%<crc16c></code> or <code>%<xmodem></code></dt>
|
||||
@ -491,10 +533,10 @@ In input, the next byte or bytes must match the checksum.
|
||||
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0xFFFFFFFF).</dd>
|
||||
<dt><code>%<crc32r></code></dt>
|
||||
<dd>Four bytes. The standard reflected 32 bit crc checksum.
|
||||
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0xFFFFFFFF).</dd>
|
||||
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0xFFFFFFFF, reflected).</dd>
|
||||
<dt><code>%<jamcrc></code></dt>
|
||||
<dd>Four bytes. Another reflected 32 bit crc checksum.
|
||||
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0x00000000).</dd>
|
||||
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0x00000000, reflected).</dd>
|
||||
<dt><code>%<adler32></code></dt>
|
||||
<dd>Four bytes. The Adler32 checksum according to <a target="ex"
|
||||
href="http://www.ietf.org/rfc/rfc1950.txt">RFC 1950</a>.</dd>
|
||||
@ -503,41 +545,49 @@ In input, the next byte or bytes must match the checksum.
|
||||
</dl>
|
||||
|
||||
<a name="regex"></a>
|
||||
<h2>12. Regular Expresion STRING Converter (<code>%/<em>regex</em>/</code>)</h2>
|
||||
<h2>13. Regular Expresion STRING Converter (<code>%/<em>regex</em>/</code>)</h2>
|
||||
<p>
|
||||
This input-only format matches <a target="ex"
|
||||
href="http://www.pcre.org/" >Perl compatible regular expressions (PCRE)</a>.
|
||||
It is only available if a PCRE library is installed.
|
||||
</p>
|
||||
<div class="box">
|
||||
<p>
|
||||
If PCRE is not available for your host or cross architecture, download
|
||||
the sourcecode from <a target="ex" href="http://www.pcre.org/">www.pcre.org</a>
|
||||
and try my EPICS compatible <a target="ex"
|
||||
href="http://epics.web.psi.ch/software/streamdevice/pcre/Makefile">Makefile</a>
|
||||
to compile it like a normal EPICS application.
|
||||
to compile it like a normal EPICS support module.
|
||||
The Makefile is known to work with EPICS 3.14.8 and PCRE 7.2.
|
||||
In your RELEASE file define the variable <code>PCRE</code> so that
|
||||
it points to the install location of PCRE.
|
||||
</p>
|
||||
<p>
|
||||
If PCRE is already installed on your system, use the variables
|
||||
<code>PCRE_INCLUDE</code> and <code>PCRE_LIB</code> instead to provide
|
||||
the install directories of <code>pcre.h</code> and the library.
|
||||
</p>
|
||||
<p>
|
||||
If you have PCRE installed in different locations for different (cross)
|
||||
architectures, define the variables in RELEASE.Common.<architecture>
|
||||
instead of the global RELEASE file.
|
||||
If PCRE is already installed on (some of) your systems, you may add
|
||||
architectures where PCRE can be found in standard include and library
|
||||
locations to the variable <code>WITH_SYSTEM_PCRE</code>.
|
||||
If either the header file or the library are in a non-standard place,
|
||||
set in your RELEASE file the variables <code>PCRE_INCLUDE_<em>arch</em></code>
|
||||
and/or <code>PCRE_LIB_<em>arch</em></code> for the respective architectures
|
||||
to the correct directories or set
|
||||
<code>PCRE_INCLUDE</code> and/or <code>PCRE_LIB</code>
|
||||
in architecture specific RELEASE.Common.<em>arch</em> files.
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
If the regular expression is not anchored, i.e. does not start with
|
||||
<code>^</code>, leading non-matching input is skipped.
|
||||
To match in multiline mode (across newlines) add <code>(?m)</code>
|
||||
at the beginning of the pattern.
|
||||
To match case insensitive, add <code>(?i)</code>.
|
||||
</p>
|
||||
<p>
|
||||
A maximum of <em>width</em> bytes is matched, if specified.
|
||||
If <em>prec</em> is given, it specifies the sub-expression whose match
|
||||
is retuned.
|
||||
If <em>precision</em> is given, it specifies the sub-expression whose match
|
||||
is returned.
|
||||
Otherwise the complete match is returned.
|
||||
In any case, the complete match is consumed from the input buffer.
|
||||
If the expression contains a <code>/</code> it must be escaped.
|
||||
If the expression contains a <code>/</code> it must be escaped like <code>\/</code>.
|
||||
</p>
|
||||
<p>
|
||||
Example: <code>%.1/<title>(.*)<\/title>/</code> returns
|
||||
@ -545,8 +595,74 @@ the title of an HTML page, skipps anything before the
|
||||
<code><title></code> tag and leaves anything after the
|
||||
<code></title></code> tag in the input buffer.
|
||||
</p>
|
||||
<a name="regsub"></a>
|
||||
<h2>14. Regular Expresion Substitution Pseudo-Converter (<code>%#/<em>regex</em>/<em>subst</em>/</code>)</h2>
|
||||
<p>
|
||||
This is a variant of the previous converter (note the <code>#</code>)
|
||||
but instead of returning the matching string,
|
||||
it can be used as a pre-processor for input or
|
||||
as a post-processor for output.
|
||||
</p>
|
||||
<p>
|
||||
Matches of the <em>regex</em> are replaced by the string <em>subst</em> with all
|
||||
<code>&</code> or <code>\0</code> in <em>subst</em> replaced with the match itself and all
|
||||
<code>\1</code> through <code>\9</code> replaced with the match of the corresponding sub-expression.
|
||||
To get a literal <code>&</code> or <code>\</code> or <code>/</code> in the substitution write
|
||||
<code>\&</code> or <code>\\</code> or <code>\/</code>.
|
||||
There is no way to specify literal bytes with values less or equal to 9 in the
|
||||
substitution!
|
||||
</p>
|
||||
<p>
|
||||
If <em>width</em> is specified, it limits the number of characters processed.
|
||||
If the <code>-</code> flag is used (i.e. <em>width</em> looks like a negative number)
|
||||
only the last <em>width</em> characters are processed, else the first.
|
||||
Without <em>width</em> (or 0) all available characters are processed.
|
||||
</p>
|
||||
<p>
|
||||
If <em>precision</em> is specified, it indicates which matches to replace.
|
||||
With the <code>+</code> flag given, <em>precision</em> is the maximum
|
||||
number of matches to replace.
|
||||
Otherwise <em>precision</em> is the index (counting from 1) of the match to replace.
|
||||
Without <em>precision</em> (or 0), all matches are replaced.
|
||||
</p>
|
||||
<p>
|
||||
In input this converter pre-processes data received from the device before
|
||||
following converters read it.
|
||||
Converters preceding this one will read unmodified input.
|
||||
Thus place this converter before those whose input should be pre-processed.
|
||||
</p>
|
||||
<p>
|
||||
In output it post-processes data already formatted by preceding converters
|
||||
before sending it to the device.
|
||||
Converters following this one will send their output unmodified.
|
||||
Thus place this converter after those whose output should be post-processed.
|
||||
</p>
|
||||
<p>
|
||||
Examples:
|
||||
<div class="indent">
|
||||
<code>%#+-10.2/ab/X/</code> replaces the string <code>ab</code> with <code>X</code>
|
||||
maximal 2 times in the last 10 characters.
|
||||
(<code>abcabcabcabc</code> becomes <code>abcXcXcabc</code>)
|
||||
</div>
|
||||
<div class="indent">
|
||||
<code>%#/\\/\//</code> replaces all <code>\</code> with <code>/</code>
|
||||
(<code>\dir\file</code> becomes <code>/dir/file</code>)
|
||||
</div>
|
||||
<div class="indent">
|
||||
<code>%#/..\B/&:/</code> inserts <code>:</code> after every second character
|
||||
which is not at the end of a word.
|
||||
(<code>0b19353134</code> becomes <code>0b:19:35:31:34</code>)
|
||||
</div>
|
||||
<div class="indent">
|
||||
<code>%#/://</code> removes all <code>:</code> characters.
|
||||
(<code>0b:19:35:31:34</code> becomes <code>0b19353134</code>)
|
||||
</div>
|
||||
<div class="indent">
|
||||
<code>%#/([^+-])*([+-])/\2\1/</code> moves a postfix sign to the front.
|
||||
(<code>1.23-</code> becomes <code>-1.23</code>)<br>
|
||||
</div>
|
||||
<a name="mantexp"></a>
|
||||
<h2>13. MantissaExponent DOUBLE converter (<code>%m</code>)</h2>
|
||||
<h2>15. MantissaExponent DOUBLE converter (<code>%m</code>)</h2>
|
||||
<p>
|
||||
This exotic and experimental format matches numbers in the format
|
||||
<i>[sign] mantissa sign exponent</i>, e.g <code>+123-4</code> meaning
|
||||
@ -565,9 +681,8 @@ Format flags <code>+</code>, <code>-</code>, and space are supported in
|
||||
the usual way (always sign, left justified, space instead of + sign).
|
||||
Flags <code>#</code> and <code>0</code> are unsupported.
|
||||
</p>
|
||||
<div class="new">
|
||||
<a name="timestamp"></a>
|
||||
<h2>14. Timestamp DOUBLE converter (<code>%T(<em>timeformat</em>)</code>)</h2>
|
||||
<h2>16. Timestamp DOUBLE converter (<code>%T(<em>timeformat</em>)</code>)</h2>
|
||||
<p>
|
||||
This format reads or writes timestamps and converts them to a double number.
|
||||
The value represents the number of seconds since 1970 (the UNIX epoch).
|
||||
@ -604,7 +719,7 @@ In output, the system function <em>strftime()</em> is used to format the time.
|
||||
There may be differences in the implementation between operating systems.
|
||||
</p>
|
||||
<p>
|
||||
In input, <em>StreamDevice</em> used its own implementation because many
|
||||
In input, <em>StreamDevice</em> uses its own implementation because many
|
||||
systems are missing the <em>strptime()</em> function and additional formats
|
||||
are supported.
|
||||
</p>
|
||||
@ -618,9 +733,8 @@ Because of the complexity of the problem, locales are not supported.
|
||||
Thus, only the English month names can be used (week day names are
|
||||
ignored anyway).
|
||||
</p>
|
||||
</div>
|
||||
<hr>
|
||||
<p align="right"><a href="processing.html">Next: Record Processing</a></p>
|
||||
<p><small>Dirk Zimoch, 2011</small></p>
|
||||
<p><small>Dirk Zimoch, 2015</small></p>
|
||||
</body>
|
||||
</html>
|
@ -104,13 +104,8 @@ This marks text you typically type in configuration files etc.
|
||||
<pre>
|
||||
Longer code segments are often set in a box.
|
||||
</pre>
|
||||
<p>
|
||||
Important modifications and new features are
|
||||
<span class="new">highlighted with a yellow background</span>.
|
||||
Obsolete features are <strike class="new">highlighted and crossed out</strike>.
|
||||
</p>
|
||||
<hr>
|
||||
<p align="right"><a href="setup.html">Next: Setup</a></p>
|
||||
<p><small>Dirk Zimoch, 2011</small></p>
|
||||
<p><small>Dirk Zimoch, 2015</small></p>
|
||||
</body>
|
||||
</html>
|
@ -1,3 +1,11 @@
|
||||
#/bin/sh
|
||||
if ! wkhtmltopdf -V >/dev/null 2>&1
|
||||
then
|
||||
echo "wkhtmltopdf not installed."
|
||||
echo "See https://wkhtmltopdf.org"
|
||||
exit 1
|
||||
fi >&2
|
||||
|
||||
PAGES="
|
||||
index.html
|
||||
setup.html
|
@ -132,6 +132,7 @@ div div div a {list-style-type:circle;}
|
||||
<a target="_parent" href="formats.html#bcd" title="Binary coded decimal LONG converter">%D</a>
|
||||
<a target="_parent" href="formats.html#chksum" title="Checksum pseudo converter">%<<em>checksum</em>></a>
|
||||
<a target="_parent" href="formats.html#regex" title="Perl regular expression STRING converter">%/<em>regex</em>/</a>
|
||||
<a target="_parent" href="formats.html#regsub" title="Perl regular expression substitution pseudo converter">%#/<em>regex</em>/<em>subst</em>/</a>
|
||||
<a target="_parent" href="formats.html#mantexp" title="MantissaExponent DOUBLE converter">%m</a>
|
||||
<a target="_parent" href="formats.html#timestamp" title="Timestamp DOUBLE converter">%T</a>
|
||||
</div>
|
@ -20,6 +20,7 @@ pre {
|
||||
border:1px solid #000000;
|
||||
white-space:pre;
|
||||
margin:2ex;
|
||||
page-break-inside:avoid;
|
||||
}
|
||||
|
||||
kbd {
|
||||
@ -88,6 +89,16 @@ code {
|
||||
text-align:left;
|
||||
}
|
||||
|
||||
.box {
|
||||
margin-left:1ex;
|
||||
margin-right:1ex;
|
||||
margin-top:0.5ex;
|
||||
padding: 0 1ex;
|
||||
border: 1px solid black;
|
||||
text-align:left;
|
||||
background-color:#f0f0f0;
|
||||
}
|
||||
|
||||
#navleft {
|
||||
position:fixed;
|
||||
left:0;
|
@ -260,7 +260,7 @@ When asked "<code>CURRENT?</code>", the device send something like
|
||||
</p>
|
||||
<p>
|
||||
<code>
|
||||
read_current {out "CURRENT?"; in "CURRENT %f A"; @mismatch {in "%(\1)39c";}}
|
||||
read_current {out "CURRENT?"; in "CURRENT %f A"; @mismatch {in "%(\$1)39c";}}
|
||||
</code>
|
||||
</p>
|
||||
<p>
|
||||
@ -281,7 +281,7 @@ With some more records, you can clean the message record if SEVR is not INVALID.
|
||||
<code>
|
||||
record (calcout, "$(DEVICE):clean_1") {<br>
|
||||
field (INPA, "$(DEVICE):readcurrent.SEVR CP")<br>
|
||||
field (CALC, "A!=2")<br>
|
||||
field (CALC, "A#3")<br>
|
||||
field (OOPT, "When Non-zero")<br>
|
||||
field (OUT, "$(DEVICE):clean_2.PROC")<br>
|
||||
}<br>
|
58
makefile
@ -1,58 +0,0 @@
|
||||
include /ioc/tools/driver.makefile
|
||||
EXCLUDE_VERSIONS = 3.13.2
|
||||
PROJECT=stream2
|
||||
BUILDCLASSES += Linux
|
||||
|
||||
#DOCUDIR = doc
|
||||
|
||||
DBDS = stream.dbd
|
||||
|
||||
BUSSES += AsynDriver
|
||||
BUSSES += Dummy
|
||||
|
||||
FORMATS += Enum
|
||||
FORMATS += BCD
|
||||
FORMATS += Raw
|
||||
FORMATS += RawFloat
|
||||
FORMATS += Binary
|
||||
FORMATS += Checksum
|
||||
FORMATS += Regexp
|
||||
FORMATS += MantissaExponent
|
||||
FORMATS += Timestamp
|
||||
|
||||
RECORDTYPES += aai aao
|
||||
RECORDTYPES += ao ai
|
||||
RECORDTYPES += bo bi
|
||||
RECORDTYPES += mbbo mbbi
|
||||
RECORDTYPES += mbboDirect mbbiDirect
|
||||
RECORDTYPES += longout longin
|
||||
RECORDTYPES += stringout stringin
|
||||
RECORDTYPES += waveform
|
||||
|
||||
SOURCES += $(RECORDTYPES:%=src/dev%Stream.c)
|
||||
SOURCES += $(FORMATS:%=src/%Converter.cc)
|
||||
SOURCES += $(BUSSES:%=src/%Interface.cc)
|
||||
SOURCES += $(wildcard src/Stream*.cc)
|
||||
SOURCES += src/StreamVersion.c
|
||||
|
||||
HEADERS += devStream.h
|
||||
HEADERS += StreamFormat.h
|
||||
HEADERS += StreamFormatConverter.h
|
||||
HEADERS += StreamBuffer.h
|
||||
HEADERS += StreamError.h
|
||||
|
||||
ifeq (${EPICS_BASETYPE},3.13)
|
||||
USR_INCLUDES += -include $(INSTALL_INCLUDE)/compat3_13.h
|
||||
endif
|
||||
ifeq (${EPICS_BASETYPE},3.14)
|
||||
RECORDTYPES += calcout
|
||||
endif
|
||||
|
||||
StreamCore.o: streamReferences
|
||||
|
||||
streamReferences:
|
||||
perl ../src/makeref.pl Interface $(BUSSES) > $@
|
||||
perl ../src/makeref.pl Converter $(FORMATS) >> $@
|
||||
|
||||
stream.dbd:
|
||||
perl ../src/makedbd.pl $(RECORDTYPES) > $@
|
@ -718,13 +718,19 @@ writeHandler()
|
||||
pasynOctet->setOutputEos(pvtOctet, pasynUser,
|
||||
NULL, 0);
|
||||
}
|
||||
pasynUser->errorMessage[0] = 0;
|
||||
status = pasynOctet->write(pvtOctet, pasynUser,
|
||||
outputBuffer, outputSize, &written);
|
||||
#ifndef NO_TEMPORARY
|
||||
debug("AsynDriverInterface::writeHandler(%s): "
|
||||
"write(..., outputSize=%ld, written=%ld) "
|
||||
"[timeout=%g sec] = %s\n",
|
||||
clientName(), (long)outputSize, (long)written,
|
||||
pasynUser->timeout, asynStatusStr[status]);
|
||||
"write(..., \"%s\", outputSize=%ld, written=%ld) "
|
||||
"[timeout=%g sec] = %s (%s)\n",
|
||||
clientName(),
|
||||
StreamBuffer(outputBuffer, outputSize).expand()(),
|
||||
(long)outputSize, (long)written,
|
||||
pasynUser->timeout, asynStatusStr[status],
|
||||
pasynUser->errorMessage);
|
||||
#endif
|
||||
|
||||
if (oldeoslen >= 0) // restore asyn terminator
|
||||
{
|
||||
@ -769,34 +775,34 @@ writeHandler()
|
||||
writeCallback(StreamIoSuccess);
|
||||
return;
|
||||
case asynTimeout:
|
||||
error("%s: asynTimeout (%g sec) in write. Asyn says: %s\n",
|
||||
error("%s: asynTimeout (%g sec) in write. Asyn driver says: \"%s\"\n",
|
||||
clientName(), pasynUser->timeout, pasynUser->errorMessage);
|
||||
writeCallback(StreamIoTimeout);
|
||||
return;
|
||||
case asynOverflow:
|
||||
error("%s: asynOverflow in write. Asyn driver says: %s\n",
|
||||
error("%s: asynOverflow in write. Asyn driver says: \"%s\"\n",
|
||||
clientName(), pasynUser->errorMessage);
|
||||
writeCallback(StreamIoFault);
|
||||
return;
|
||||
case asynError:
|
||||
error("%s: asynError in write. Asyn driver says: %s\n",
|
||||
error("%s: asynError in write. Asyn driver says: \"%s\"\n",
|
||||
clientName(), pasynUser->errorMessage);
|
||||
writeCallback(StreamIoFault);
|
||||
return;
|
||||
#ifdef ASYN_VERSION // asyn >= 4.14
|
||||
case asynDisconnected:
|
||||
error("%s: asynDisconnected in write. Asyn driver says: %s\n",
|
||||
error("%s: asynDisconnected in write. Asyn driver says: \"%s\"\n",
|
||||
clientName(), pasynUser->errorMessage);
|
||||
writeCallback(StreamIoFault);
|
||||
return;
|
||||
case asynDisabled:
|
||||
error("%s: asynDisconnected in write. Asyn driver says: %s\n",
|
||||
error("%s: asynDisconnected in write. Asyn driver says: \"%s\"\n",
|
||||
clientName(), pasynUser->errorMessage);
|
||||
writeCallback(StreamIoFault);
|
||||
return;
|
||||
#endif
|
||||
default:
|
||||
error("%s: unknown asyn error in write. Asyn driver says: %s\n",
|
||||
error("%s: unknown asyn error in write. Asyn driver says: \"%s\"\n",
|
||||
clientName(), pasynUser->errorMessage);
|
||||
writeCallback(StreamIoFault);
|
||||
return;
|
||||
@ -887,6 +893,11 @@ readHandler()
|
||||
oldeoslen = -1;
|
||||
} else do {
|
||||
// device (e.g. GPIB) might not accept full eos length
|
||||
if ((int)deveoslen == oldeoslen && strcmp(deveos, oldeos) == 0)
|
||||
{
|
||||
// nothing to do: old and new eos are the same
|
||||
break;
|
||||
}
|
||||
if (pasynOctet->setInputEos(pvtOctet, pasynUser,
|
||||
deveos, deveoslen) == asynSuccess)
|
||||
{
|
||||
@ -894,8 +905,9 @@ readHandler()
|
||||
if (ioAction != AsyncRead)
|
||||
{
|
||||
debug("AsynDriverInterface::readHandler(%s) "
|
||||
"input EOS set to %s\n",
|
||||
"input EOS changed from \"%s\" to \"%s\"\n",
|
||||
clientName(),
|
||||
StreamBuffer(oldeos, oldeoslen).expand()(),
|
||||
StreamBuffer(deveos, deveoslen).expand()());
|
||||
}
|
||||
#endif
|
||||
@ -951,6 +963,7 @@ readHandler()
|
||||
readMore = 0;
|
||||
received = 0;
|
||||
eomReason = 0;
|
||||
pasynUser->errorMessage[0] = 0;
|
||||
|
||||
debug("AsynDriverInterface::readHandler(%s): ioAction=%s "
|
||||
"read(..., bytesToRead=%ld, ...) "
|
||||
@ -959,12 +972,13 @@ readHandler()
|
||||
bytesToRead, pasynUser->timeout);
|
||||
status = pasynOctet->read(pvtOctet, pasynUser,
|
||||
buffer, bytesToRead, &received, &eomReason);
|
||||
#ifndef NO_TEMPORARY
|
||||
debug("AsynDriverInterface::readHandler(%s): "
|
||||
"read returned %s: ioAction=%s received=%ld, eomReason=%s, buffer=\"%s\"\n",
|
||||
clientName(), asynStatusStr[status], ioActionStr[ioAction],
|
||||
(long)received,eomReasonStr[eomReason&0x7],
|
||||
StreamBuffer(buffer, received).expand()());
|
||||
|
||||
#endif
|
||||
pasynManager->isConnected(pasynUser, &connected);
|
||||
debug("AsynDriverInterface::readHandler(%s): "
|
||||
"device is now %sconnected\n",
|
||||
@ -1094,29 +1108,29 @@ readHandler()
|
||||
}
|
||||
peeksize = inputBuffer.capacity();
|
||||
// deliver whatever we could save
|
||||
error("%s: asynOverflow in read. Asyn driver says: %s\n",
|
||||
error("%s: asynOverflow in read. Asyn driver says: \"%s\"\n",
|
||||
clientName(), pasynUser->errorMessage);
|
||||
readCallback(StreamIoFault, buffer, received);
|
||||
break;
|
||||
case asynError:
|
||||
error("%s: asynError in read. Asyn driver says: %s\n",
|
||||
error("%s: asynError in read. Asyn driver says: \"%s\"\n",
|
||||
clientName(), pasynUser->errorMessage);
|
||||
readCallback(StreamIoFault, buffer, received);
|
||||
break;
|
||||
#ifdef ASYN_VERSION // asyn >= 4.14
|
||||
case asynDisconnected:
|
||||
error("%s: asynDisconnected in read. Asyn driver says: %s\n",
|
||||
error("%s: asynDisconnected in read. Asyn driver says: \"%s\"\n",
|
||||
clientName(), pasynUser->errorMessage);
|
||||
readCallback(StreamIoFault);
|
||||
return;
|
||||
case asynDisabled:
|
||||
error("%s: asynDisconnected in read. Asyn driver says: %s\n",
|
||||
error("%s: asynDisconnected in read. Asyn driver says: \"%s\"\n",
|
||||
clientName(), pasynUser->errorMessage);
|
||||
readCallback(StreamIoFault);
|
||||
return;
|
||||
#endif
|
||||
default:
|
||||
error("%s: unknown asyn error in read. Asyn driver says: %s\n",
|
||||
error("%s: unknown asyn error in read. Asyn driver says: \"%s\"\n",
|
||||
clientName(), pasynUser->errorMessage);
|
||||
readCallback(StreamIoFault);
|
||||
return;
|
||||
@ -1138,10 +1152,16 @@ readHandler()
|
||||
}
|
||||
|
||||
// restore original EOS
|
||||
if (oldeoslen >= 0)
|
||||
if (oldeoslen >= 0 && oldeoslen != (int)deveoslen && strcmp(deveos, oldeos) != 0)
|
||||
{
|
||||
pasynOctet->setInputEos(pvtOctet, pasynUser,
|
||||
oldeos, oldeoslen);
|
||||
#ifndef NO_TEMPORARY
|
||||
debug("AsynDriverInterface::readHandler(%s) "
|
||||
"input EOS restored to \"%s\"\n",
|
||||
clientName(),
|
||||
StreamBuffer(oldeos, oldeoslen).expand()());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,9 +31,9 @@ class BCDConverter : public StreamFormatConverter
|
||||
};
|
||||
|
||||
int BCDConverter::
|
||||
parse(const StreamFormat&, StreamBuffer&, const char*&, bool)
|
||||
parse(const StreamFormat& fmt, StreamBuffer&, const char*&, bool)
|
||||
{
|
||||
return long_format;
|
||||
return (fmt.flags & sign_flag) ? signed_format : unsigned_format;
|
||||
}
|
||||
|
||||
bool BCDConverter::
|
||||
|
@ -19,6 +19,7 @@
|
||||
***************************************************************/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include "StreamFormatConverter.h"
|
||||
#include "StreamError.h"
|
||||
|
||||
@ -32,11 +33,16 @@ class BinaryConverter : public StreamFormatConverter
|
||||
};
|
||||
|
||||
int BinaryConverter::
|
||||
parse(const StreamFormat& format, StreamBuffer& info,
|
||||
parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
const char*& source, bool)
|
||||
{
|
||||
if (format.conv == 'B')
|
||||
if (fmt.conv == 'b')
|
||||
{
|
||||
// default characters 0 and 1 for %b
|
||||
info.append("01");
|
||||
return unsigned_format;
|
||||
}
|
||||
|
||||
// user defined characters for %B (next 2 in source)
|
||||
if (*source)
|
||||
{
|
||||
@ -46,37 +52,40 @@ parse(const StreamFormat& format, StreamBuffer& info,
|
||||
{
|
||||
if (*source == esc) source++;
|
||||
info.append(*source++);
|
||||
return long_format;
|
||||
return unsigned_format;
|
||||
}
|
||||
}
|
||||
error("Missing characters after %%B format conversion\n");
|
||||
return false;
|
||||
}
|
||||
// default characters for %b
|
||||
info.append("01");
|
||||
return long_format;
|
||||
}
|
||||
|
||||
bool BinaryConverter::
|
||||
printLong(const StreamFormat& format, StreamBuffer& output, long value)
|
||||
printLong(const StreamFormat& fmt, StreamBuffer& output, long value)
|
||||
{
|
||||
int prec = format.prec;
|
||||
int prec = fmt.prec;
|
||||
if (prec == -1)
|
||||
{
|
||||
// find number of significant bits
|
||||
prec = sizeof (value) * 8;
|
||||
while (prec && (value & (1L << (prec - 1))) == 0) prec--;
|
||||
// Find number of significant bits is nothing is specified.
|
||||
unsigned long x = (unsigned long) value;
|
||||
prec = 32;
|
||||
#if (LONG_BIT > 32)
|
||||
if (x > 0xFFFFFFFF) { prec = 64; x >>=32; }
|
||||
#endif
|
||||
if (x <= 0x0000FFFF) { prec -= 16; x <<=16; }
|
||||
if (x <= 0x00FFFFFF) { prec -= 8; x <<=8; }
|
||||
if (x <= 0x0FFFFFFF) { prec -= 4; x <<=4; }
|
||||
if (x <= 0x3FFFFFFF) { prec -= 2; x <<=2; }
|
||||
if (x <= 0x7FFFFFFF) { prec -= 1; }
|
||||
}
|
||||
if (prec == 0) prec++; // print at least one bit
|
||||
int width = prec;
|
||||
if (format.width > width) width = format.width;
|
||||
char zero = format.info[0];
|
||||
char one = format.info[1];
|
||||
char fill = (format.flags & zero_flag) ? zero : ' ';
|
||||
if (format.flags & alt_flag)
|
||||
if (fmt.width > width) width = fmt.width;
|
||||
char zero = fmt.info[0];
|
||||
char one = fmt.info[1];
|
||||
char fill = (fmt.flags & zero_flag) ? zero : ' ';
|
||||
if (fmt.flags & alt_flag)
|
||||
{
|
||||
// little endian (least significant bit first)
|
||||
if (!(format.flags & left_flag))
|
||||
if (!(fmt.flags & left_flag))
|
||||
{
|
||||
// pad left
|
||||
while (width > prec)
|
||||
@ -100,7 +109,7 @@ printLong(const StreamFormat& format, StreamBuffer& output, long value)
|
||||
else
|
||||
{
|
||||
// big endian (most significant bit first)
|
||||
if (!(format.flags & left_flag))
|
||||
if (!(fmt.flags & left_flag))
|
||||
{
|
||||
// pad left
|
||||
while (width > prec)
|
||||
@ -124,18 +133,18 @@ printLong(const StreamFormat& format, StreamBuffer& output, long value)
|
||||
}
|
||||
|
||||
int BinaryConverter::
|
||||
scanLong(const StreamFormat& format, const char* input, long& value)
|
||||
scanLong(const StreamFormat& fmt, const char* input, long& value)
|
||||
{
|
||||
long val = 0;
|
||||
int width = format.width;
|
||||
int width = fmt.width;
|
||||
if (width == 0) width = -1;
|
||||
int length = 0;
|
||||
char zero = format.info[0];
|
||||
char one = format.info[1];
|
||||
char zero = fmt.info[0];
|
||||
char one = fmt.info[1];
|
||||
if (!isspace(zero) && !isspace(one))
|
||||
while (isspace(input[length])) length++; // skip whitespaces
|
||||
if (input[length] != zero && input[length] != one) return -1;
|
||||
if (format.flags & alt_flag)
|
||||
if (fmt.flags & alt_flag)
|
||||
{
|
||||
// little endian (least significan bit first)
|
||||
long mask = 1;
|
||||
|
@ -3,17 +3,17 @@
|
||||
|
||||
# You may add more record interfaces
|
||||
# This requires the naming conventions
|
||||
# dev$(RECORD)Stream.c
|
||||
# dev$(RECORDTYPE)Stream.c
|
||||
|
||||
RECORDS += ao ai
|
||||
RECORDS += bo bi
|
||||
RECORDS += mbbo mbbi
|
||||
RECORDS += mbboDirect mbbiDirect
|
||||
RECORDS += longout longin
|
||||
RECORDS += stringout stringin
|
||||
RECORDS += waveform
|
||||
RECORDS += calcout
|
||||
RECORDS += aai aao
|
||||
RECORDTYPES += ao ai
|
||||
RECORDTYPES += bo bi
|
||||
RECORDTYPES += mbbo mbbi
|
||||
RECORDTYPES += mbboDirect mbbiDirect
|
||||
RECORDTYPES += longout longin
|
||||
RECORDTYPES += stringout stringin
|
||||
RECORDTYPES += waveform
|
||||
RECORDTYPES += calcout
|
||||
RECORDTYPES += aai aao
|
||||
|
||||
# Do you have synApps and want support for scalcout?
|
||||
# Then define CALC or SYNAPPS in your RELEASE file
|
||||
@ -21,18 +21,23 @@ RECORDS += aai aao
|
||||
# Due to strange cross dependencies in synApps
|
||||
# you have to build the 'sscan' and 'genSub'
|
||||
# modules before building 'calc'.
|
||||
# See doc/scalcout.html for a required fix in scalcout.
|
||||
|
||||
SYNAPPS_RECORDS += scalcout
|
||||
# The 'calc' version must be 2-4 or higher.
|
||||
# Up to version 2-6 (synApps 5.1) the 'calc' module needs a fix.
|
||||
# See doc/scalcout.html for details.
|
||||
ifneq ($(words $(CALC) $(SYNAPPS)), 0)
|
||||
RECORDTYPES += scalcout
|
||||
endif
|
||||
|
||||
# You may add more bus interfaces
|
||||
# This requires the naming convention
|
||||
# $(BUS)Interface.cc
|
||||
# asynDriver interface is added automatically
|
||||
# if ASYN is defined in your RELEASE file.
|
||||
# asynDriver interface is added if ASYN is defined in your RELEASE file.
|
||||
|
||||
BUSSES += Debug
|
||||
BUSSES += Dummy
|
||||
ifdef ASYN
|
||||
BUSSES += AsynDriver
|
||||
endif
|
||||
|
||||
# You may add more format converters
|
||||
# This requires the naming convention
|
||||
|
@ -22,9 +22,21 @@
|
||||
#include "StreamError.h"
|
||||
#if defined(__vxworks) || defined(vxWorks) || defined(_WIN32) || defined(__rtems__)
|
||||
// These systems have no strncasecmp
|
||||
#include <epicsVersion.h>
|
||||
#ifdef BASE_VERSION
|
||||
// 3.13
|
||||
#include <ctype.h>
|
||||
static int strncasecmp(const char *s1, const char *s2, size_t n)
|
||||
{
|
||||
int r=0;
|
||||
while (n && (r = toupper(*s1)-toupper(*s2)) == 0) { n--; s1++; s2++; };
|
||||
return r;
|
||||
}
|
||||
#else
|
||||
#include <epicsString.h>
|
||||
#define strncasecmp epicsStrnCaseCmp
|
||||
#endif
|
||||
#endif
|
||||
#include <ctype.h>
|
||||
|
||||
typedef unsigned int (*checksumFunc)(const unsigned char* data, unsigned int len, unsigned int init);
|
||||
@ -469,6 +481,7 @@ static checksum checksumMap[] =
|
||||
{"ccitt8", crc_0x31, 0x00, 0x00, 1}, // 0xA1
|
||||
{"crc16", crc_0x8005, 0x0000, 0x0000, 2}, // 0xFEE8
|
||||
{"crc16r", crc_0x8005_r, 0x0000, 0x0000, 2}, // 0xBB3D
|
||||
{"modbus", crc_0x8005_r, 0xFFFF, 0x0000, 2}, // 0x4B37
|
||||
{"ccitt16", crc_0x1021, 0xFFFF, 0x0000, 2}, // 0x29B1
|
||||
{"ccitt16a",crc_0x1021, 0x1D0F, 0x0000, 2}, // 0xE5CC
|
||||
{"ccitt16x",crc_0x1021, 0x0000, 0x0000, 2}, // 0x31C3
|
||||
|
@ -36,7 +36,7 @@ class EnumConverter : public StreamFormatConverter
|
||||
|
||||
int EnumConverter::
|
||||
parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
const char*& source, bool)
|
||||
const char*& source, bool scanFormat)
|
||||
{
|
||||
if (fmt.flags & (left_flag|sign_flag|space_flag|zero_flag))
|
||||
{
|
||||
@ -56,7 +56,30 @@ parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
if (*source == '=' && (fmt.flags & alt_flag))
|
||||
{
|
||||
char* p;
|
||||
index = strtol(++source, &p, 0);
|
||||
|
||||
if (*++source == '?')
|
||||
{
|
||||
// default choice
|
||||
if (scanFormat)
|
||||
{
|
||||
error("Default value only allowed in output formats\n");
|
||||
return false;
|
||||
}
|
||||
if (*++source != '}')
|
||||
{
|
||||
error("Default value must be last\n");
|
||||
return false;
|
||||
}
|
||||
source++;
|
||||
numEnums = -(numEnums+1);
|
||||
info.append('\0');
|
||||
memcpy(info(n), &numEnums, sizeof(numEnums));
|
||||
debug("EnumConverter::parse %ld choices with default: %s\n",
|
||||
-numEnums, info.expand()());
|
||||
return enum_format;
|
||||
}
|
||||
|
||||
index = strtol(source, &p, 0);
|
||||
if (p == source || (*p != '|' && *p != '}'))
|
||||
{
|
||||
error("Integer expected after '=' "
|
||||
@ -99,6 +122,9 @@ printLong(const StreamFormat& fmt, StreamBuffer& output, long value)
|
||||
const char* s = fmt.info;
|
||||
long numEnums = extract<long>(s);
|
||||
long index = extract<long>(s);
|
||||
bool noDefault = numEnums >= 0;
|
||||
|
||||
if (numEnums < 0) numEnums=-numEnums-1;
|
||||
while (numEnums-- && (value != index))
|
||||
{
|
||||
while(*s)
|
||||
@ -109,7 +135,7 @@ printLong(const StreamFormat& fmt, StreamBuffer& output, long value)
|
||||
s++;
|
||||
index = extract<long>(s);
|
||||
}
|
||||
if (numEnums == -1)
|
||||
if (numEnums == -1 && noDefault)
|
||||
{
|
||||
error("Value %li not found in enum set\n", value);
|
||||
return false;
|
||||
|
17
src/Makefile
@ -18,7 +18,7 @@
|
||||
# #
|
||||
################################################################
|
||||
|
||||
TOP=../..
|
||||
TOP:=../$(TOP)
|
||||
|
||||
# Look if we have EPICS R3.13 or R3.14
|
||||
ifeq ($(wildcard $(TOP)/configure),)
|
||||
@ -40,13 +40,8 @@ DBD += $(LIBRARY_DEFAULT).dbd
|
||||
|
||||
ifdef ASYN
|
||||
LIB_LIBS += asyn
|
||||
BUSSES += AsynDriver
|
||||
endif
|
||||
|
||||
ifdef T_A
|
||||
ifndef BUSSES
|
||||
$(error No bus interface defined! Didn't you set ASYN in your RELEASE file?)
|
||||
endif
|
||||
else
|
||||
$(warning Asyn not included! Didn't you set ASYN in your RELEASE file?)
|
||||
endif
|
||||
|
||||
ifeq ($(LOADABLE_MODULE),YES)
|
||||
@ -54,7 +49,7 @@ SRCS += $(LIBRARY_DEFAULT)_registerRecordDeviceDriver.cpp
|
||||
endif
|
||||
SRCS += $(BUSSES:%=%Interface.cc)
|
||||
SRCS += $(FORMATS:%=%Converter.cc)
|
||||
SRCS += $(RECORDS:%=dev%Stream.c)
|
||||
SRCS += $(RECORDTYPES:%=dev%Stream.c)
|
||||
SRCS += $(STREAM_SRCS)
|
||||
|
||||
# find system wide or local PCRE header and library
|
||||
@ -89,9 +84,9 @@ streamReferences: ../CONFIG_STREAM
|
||||
$(PERL) ../makeref.pl Interface $(BUSSES) > $@
|
||||
$(PERL) ../makeref.pl Converter $(FORMATS) >> $@
|
||||
|
||||
# create stream.dbd from all RECORDS
|
||||
# create stream.dbd from all RECORDTYPES
|
||||
$(COMMON_DIR)/$(LIBRARY_DEFAULT).dbd: ../CONFIG_STREAM
|
||||
$(PERL) ../makedbd.pl $(RECORDS) > $@
|
||||
$(PERL) ../makedbd.pl $(RECORDTYPES) > $@
|
||||
|
||||
$(LIBRARY_DEFAULT).dbd$(DEP): ../CONFIG_STREAM
|
||||
echo $(LIBRARY_DEFAULT).dbd: $< > $@
|
||||
|
@ -18,14 +18,11 @@
|
||||
# #
|
||||
################################################################
|
||||
|
||||
TOP = ../../..
|
||||
TOP := ../../$(TOP)
|
||||
|
||||
include $(TOP)/config/CONFIG_APP
|
||||
include ../CONFIG_STREAM
|
||||
|
||||
# In 3.13, calcout has no device support
|
||||
RECORDS_3_13 = $(filter-out calcout,$(RECORDS))
|
||||
|
||||
DBDNAME = stream.dbd
|
||||
|
||||
INC += devStream.h
|
||||
@ -35,8 +32,9 @@ INC += devStream.h
|
||||
CONFIGS = RULES.munch
|
||||
SCRIPTS = munch.pl
|
||||
|
||||
include $(TOP)/config/RULES.Host
|
||||
include $(EPICS_BASE)/config/RULES.Host
|
||||
|
||||
# create stream.dbd from all RECORDS
|
||||
# create stream.dbd from all RECORDTYPES
|
||||
# In 3.13, calcout has no device support
|
||||
stream.dbd: ../CONFIG_STREAM
|
||||
$(PERL) ../makedbd.pl -3.13 $(RECORDS_3_13) > $@
|
||||
$(PERL) ../makedbd.pl -3.13 $(filter-out calcout,$(RECORDTYPES)) > $@
|
||||
|
@ -18,29 +18,23 @@
|
||||
# #
|
||||
################################################################
|
||||
|
||||
TOP = ../../..
|
||||
TOP := ../../$(TOP)
|
||||
|
||||
include $(TOP)/config/CONFIG_APP
|
||||
include ../CONFIG_STREAM
|
||||
|
||||
LIBNAME = streamLib
|
||||
|
||||
# In 3.13, calcout has no device support
|
||||
RECORDS_3_13 = $(filter-out calcout,$(RECORDS))
|
||||
|
||||
ifdef ASYN
|
||||
BUSSES += AsynDriver
|
||||
endif
|
||||
|
||||
SRCS.cc += $(patsubst %,../%,$(filter %.cc,$(STREAM_SRCS)))
|
||||
SRCS.cc += $(BUSSES:%=../%Interface.cc)
|
||||
SRCS.cc += $(FORMATS:%=../%Converter.cc)
|
||||
SRCS.c += $(patsubst %,../%,$(filter %.c,$(STREAM_SRCS)))
|
||||
SRCS.c += $(RECORDS_3_13:%=../dev%Stream.c)
|
||||
# In 3.13, calcout has no device support
|
||||
SRCS.c += $(patsubst %,../dev%Stream.c,$(filter-out calcout,$(RECORDTYPES)))
|
||||
|
||||
LIBOBJS = $(patsubst ../%,%.o,$(basename $(SRCS.cc) $(SRCS.c)))
|
||||
|
||||
include $(TOP)/config/RULES.Vx
|
||||
include $(EPICS_BASE)/config/RULES.Vx
|
||||
include $(TOP)/config/RULES.munch
|
||||
|
||||
build:: depends
|
||||
|
@ -31,23 +31,23 @@ class RawConverter : public StreamFormatConverter
|
||||
};
|
||||
|
||||
int RawConverter::
|
||||
parse(const StreamFormat&, StreamBuffer&,
|
||||
parse(const StreamFormat& fmt, StreamBuffer&,
|
||||
const char*&, bool)
|
||||
{
|
||||
return long_format;
|
||||
return (fmt.flags & (sign_flag|zero_flag)) ? signed_format : unsigned_format;
|
||||
}
|
||||
|
||||
bool RawConverter::
|
||||
printLong(const StreamFormat& format, StreamBuffer& output, long value)
|
||||
printLong(const StreamFormat& fmt, StreamBuffer& output, long value)
|
||||
{
|
||||
int prec = format.prec; // number of bytes from value
|
||||
int prec = fmt.prec; // number of bytes from value
|
||||
if (prec == -1) prec = 1; // default: 1 byte
|
||||
int width = prec; // number of bytes in output
|
||||
if (prec > (int)sizeof(long)) prec=sizeof(long);
|
||||
if (format.width > width) width = format.width;
|
||||
if (fmt.width > width) width = fmt.width;
|
||||
|
||||
char byte = 0;
|
||||
if (format.flags & alt_flag) // little endian (lsb first)
|
||||
if (fmt.flags & alt_flag) // little endian (lsb first)
|
||||
{
|
||||
while (prec--)
|
||||
{
|
||||
@ -56,7 +56,7 @@ printLong(const StreamFormat& format, StreamBuffer& output, long value)
|
||||
value >>= 8;
|
||||
width--;
|
||||
}
|
||||
if (format.flags & zero_flag)
|
||||
if (fmt.flags & zero_flag)
|
||||
{
|
||||
// fill with zero
|
||||
byte = 0;
|
||||
@ -73,7 +73,7 @@ printLong(const StreamFormat& format, StreamBuffer& output, long value)
|
||||
}
|
||||
else // big endian (msb first)
|
||||
{
|
||||
if (format.flags & zero_flag)
|
||||
if (fmt.flags & zero_flag)
|
||||
{
|
||||
// fill with zero
|
||||
byte = 0;
|
||||
@ -97,17 +97,17 @@ printLong(const StreamFormat& format, StreamBuffer& output, long value)
|
||||
}
|
||||
|
||||
int RawConverter::
|
||||
scanLong(const StreamFormat& format, const char* input, long& value)
|
||||
scanLong(const StreamFormat& fmt, const char* input, long& value)
|
||||
{
|
||||
long length = 0;
|
||||
long val = 0;
|
||||
int width = format.width;
|
||||
int width = fmt.width;
|
||||
if (width == 0) width = 1; // default: 1 byte
|
||||
if (format.flags & skip_flag)
|
||||
if (fmt.flags & skip_flag)
|
||||
{
|
||||
return width; // just skip input
|
||||
}
|
||||
if (format.flags & alt_flag)
|
||||
if (fmt.flags & alt_flag)
|
||||
{
|
||||
// little endian (lsb first)
|
||||
unsigned int shift = 0;
|
||||
@ -118,7 +118,7 @@ scanLong(const StreamFormat& format, const char* input, long& value)
|
||||
}
|
||||
if (width == 0)
|
||||
{
|
||||
if (format.flags & zero_flag)
|
||||
if (fmt.flags & zero_flag)
|
||||
{
|
||||
// fill with zero
|
||||
val |= ((unsigned char) input[length++]) << shift;
|
||||
@ -134,7 +134,7 @@ scanLong(const StreamFormat& format, const char* input, long& value)
|
||||
else
|
||||
{
|
||||
// big endian (msb first)
|
||||
if (format.flags & zero_flag)
|
||||
if (fmt.flags & zero_flag)
|
||||
{
|
||||
// fill with zero
|
||||
val = (unsigned char) input[length++];
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include "string.h"
|
||||
#include "pcre.h"
|
||||
|
||||
// Perl regular expressions (PCRE) %/regexp/
|
||||
// Perl regular expressions (PCRE) %/regexp/ and %#/regexp/subst/
|
||||
|
||||
/* Notes:
|
||||
- Memory for compiled regexp is allocated in parse but never freed.
|
||||
@ -37,15 +37,17 @@
|
||||
|
||||
class RegexpConverter : public StreamFormatConverter
|
||||
{
|
||||
int parse (const StreamFormat&, StreamBuffer&, const char*&, bool);
|
||||
int scanString(const StreamFormat&, const char*, char*, size_t);
|
||||
int parse (const StreamFormat& fmt, StreamBuffer&, const char*&, bool);
|
||||
int scanString(const StreamFormat& fmt, const char*, char*, size_t);
|
||||
int scanPseudo(const StreamFormat& fmt, StreamBuffer& input, long& cursor);
|
||||
bool printPseudo(const StreamFormat& fmt, StreamBuffer& output);
|
||||
};
|
||||
|
||||
int RegexpConverter::
|
||||
parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
const char*& source, bool scanFormat)
|
||||
{
|
||||
if (!scanFormat)
|
||||
if (!scanFormat && !(fmt.flags & alt_flag))
|
||||
{
|
||||
error("Format conversion %%/regexp/ is only allowed in input formats\n");
|
||||
return false;
|
||||
@ -55,28 +57,31 @@ parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
error("Subexpression index %d too big (>9)\n", fmt.prec);
|
||||
return false;
|
||||
}
|
||||
if (fmt.flags & (left_flag|space_flag|zero_flag|alt_flag))
|
||||
{
|
||||
error("Use of modifiers '-', ' ', '0', '#'"
|
||||
"not allowed with %%/regexp/ conversion\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
StreamBuffer pattern;
|
||||
while (*source != '/')
|
||||
{
|
||||
if (!*source) {
|
||||
error("Missing closing '/' after %%/ format conversion\n");
|
||||
error("Missing closing '/' after %%/%s format conversion\n", pattern());
|
||||
return false;
|
||||
}
|
||||
if (*source == esc) {
|
||||
source++;
|
||||
pattern.print("\\x%02x", *source++ & 0xFF);
|
||||
if (*source == esc) { // handle escaped chars
|
||||
if (*++source != '/') // just un-escape /
|
||||
{
|
||||
pattern.append('\\');
|
||||
if ((*source & 0x7f) < 0x30) // handle control chars
|
||||
{
|
||||
pattern.print("x%02x", *source++);
|
||||
continue;
|
||||
}
|
||||
// fall through for PCRE codes like \B
|
||||
}
|
||||
}
|
||||
pattern.append(*source++);
|
||||
}
|
||||
source++;
|
||||
debug("regexp = \"%s\"\n", pattern());
|
||||
debug("regexp = \"%s\"\n", pattern.expand()());
|
||||
|
||||
const char* errormsg;
|
||||
int eoffset;
|
||||
pcre* code = pcre_compile(pattern(), 0,
|
||||
@ -87,6 +92,26 @@ parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
return false;
|
||||
}
|
||||
info.append(&code, sizeof(code));
|
||||
|
||||
if (fmt.flags & alt_flag)
|
||||
{
|
||||
StreamBuffer subst;
|
||||
debug("check for subst in \"%s\"\n", StreamBuffer(source).expand()());
|
||||
while (*source != '/')
|
||||
{
|
||||
if (!*source) {
|
||||
error("Missing closing '/' after %%#/%s/%s format conversion\n", pattern(), subst());
|
||||
return false;
|
||||
}
|
||||
if (*source == esc)
|
||||
subst.append(*source++);
|
||||
subst.append(*source++);
|
||||
}
|
||||
source++;
|
||||
debug("subst = \"%s\"\n", subst.expand()());
|
||||
info.append(subst).append('\0');
|
||||
return pseudo_format;
|
||||
}
|
||||
return string_format;
|
||||
}
|
||||
|
||||
@ -94,32 +119,130 @@ int RegexpConverter::
|
||||
scanString(const StreamFormat& fmt, const char* input,
|
||||
char* value, size_t maxlen)
|
||||
{
|
||||
pcre* code;
|
||||
size_t len;
|
||||
int ovector[30];
|
||||
int rc;
|
||||
int subexpr = 0;
|
||||
unsigned int l;
|
||||
|
||||
memcpy (&code, fmt.info, sizeof(code));
|
||||
const char* info = fmt.info;
|
||||
pcre* code = extract<pcre*>(info);
|
||||
int length = fmt.width > 0 ? fmt.width : strlen(input);
|
||||
int subexpr = fmt.prec > 0 ? fmt.prec : 0;
|
||||
|
||||
len = fmt.width > 0 ? fmt.width : strlen(input);
|
||||
subexpr = fmt.prec > 0 ? fmt.prec : 0;
|
||||
rc = pcre_exec(code, NULL, input, len, 0, 0, ovector, 30);
|
||||
if (rc < 1) return -1;
|
||||
if (fmt.flags & skip_flag) return ovector[1];
|
||||
len = ovector[subexpr*2+1] - ovector[subexpr*2];
|
||||
if (len >= maxlen) {
|
||||
if (!(fmt.flags & sign_flag)) {
|
||||
error("Regexp: Matching string \"%s\" too long (%d>%d bytes). You may want to try the + flag: \"%%+/.../\"\n",
|
||||
StreamBuffer(input+ovector[subexpr*2], len).expand()(),
|
||||
(int)len, (int)maxlen-1);
|
||||
debug("input = \"%s\"\n", input);
|
||||
debug("length=%d\n", length);
|
||||
|
||||
rc = pcre_exec(code, NULL, input, length, 0, 0, ovector, 30);
|
||||
debug("pcre_exec match \"%.*s\" result = %d\n", length, input, rc);
|
||||
if ((subexpr && rc <= subexpr) || rc < 0)
|
||||
{
|
||||
// error or no match or not enough sub-expressions
|
||||
return -1;
|
||||
}
|
||||
len = maxlen-1;
|
||||
if (fmt.flags & skip_flag) return ovector[subexpr*2+1];
|
||||
|
||||
l = ovector[subexpr*2+1] - ovector[subexpr*2];
|
||||
if (l >= maxlen) {
|
||||
if (!(fmt.flags & sign_flag)) {
|
||||
error("Regexp: Matching string \"%s\" too long (%d>%ld bytes). You may want to try the + flag: \"%%+/.../\"\n",
|
||||
StreamBuffer(input + ovector[subexpr*2],l).expand()(),
|
||||
l, (long)maxlen-1);
|
||||
return -1;
|
||||
}
|
||||
memcpy(value, input+ovector[subexpr*2], len);
|
||||
value[len]=0;
|
||||
return ovector[1];
|
||||
l = maxlen-1;
|
||||
}
|
||||
memcpy(value, input + ovector[subexpr*2], l);
|
||||
value[l] = '\0';
|
||||
return ovector[1]; // consume input until end of match
|
||||
}
|
||||
|
||||
static void regsubst(const StreamFormat& fmt, StreamBuffer& buffer, long start)
|
||||
{
|
||||
const char* subst = fmt.info;
|
||||
pcre* code = extract<pcre*>(subst);
|
||||
long length;
|
||||
int rc, l, c, r, rl, n;
|
||||
int ovector[30];
|
||||
StreamBuffer s;
|
||||
|
||||
length = buffer.length() - start;
|
||||
if (fmt.width && fmt.width < length)
|
||||
length = fmt.width;
|
||||
if (fmt.flags & sign_flag)
|
||||
start = buffer.length() - length;
|
||||
|
||||
debug("regsubst buffer=\"%s\", start=%ld, length=%ld, subst = \"%s\"\n",
|
||||
buffer.expand()(), start, length, subst);
|
||||
|
||||
for (c = 0, n = 1; c < length; n++)
|
||||
{
|
||||
rc = pcre_exec(code, NULL, buffer(start+c), length-c, 0, 0, ovector, 30);
|
||||
debug("pcre_exec match \"%.*s\" result = %d\n", (int)length-c, buffer(start+c), rc);
|
||||
if (rc < 0) // no match
|
||||
return;
|
||||
|
||||
if (!(fmt.flags & sign_flag) && n < fmt.prec) // without + flag
|
||||
{
|
||||
// do not yet replace this match
|
||||
c += ovector[1];
|
||||
continue;
|
||||
}
|
||||
// replace & by match in subst
|
||||
l = ovector[1] - ovector[0];
|
||||
debug("start = \"%s\"\n", buffer(start+c));
|
||||
debug("match = \"%.*s\"\n", l, buffer(start+c+ovector[0]));
|
||||
for (r = 1; r < rc; r++)
|
||||
debug("sub%d = \"%.*s\"\n", r, ovector[r*2+1]-ovector[r*2], buffer(start+c+ovector[r*2]));
|
||||
debug("rest = \"%s\"\n", buffer(start+c+ovector[1]));
|
||||
s = subst;
|
||||
debug("subs = \"%s\"\n", s.expand()());
|
||||
for (r = 0; r < s.length(); r++)
|
||||
{
|
||||
debug("check \"%s\"\n", s.expand(r)());
|
||||
if (s[r] == esc)
|
||||
{
|
||||
unsigned char ch = s[r+1];
|
||||
if (ch < 9) // escaped 0 - 9 : replace with subexpr
|
||||
{
|
||||
ch *= 2;
|
||||
rl = ovector[ch+1] - ovector[ch];
|
||||
debug("replace \\%d: \"%.*s\"\n", ch/2, rl, buffer(start+c+ovector[ch]));
|
||||
s.replace(r, 2, buffer(start+c+ovector[ch]), rl);
|
||||
r += rl - 1;
|
||||
}
|
||||
else
|
||||
s.remove(r, 1); // just remove escape
|
||||
}
|
||||
else if (s[r] == '&') // unescaped & : replace with match
|
||||
{
|
||||
debug("replace &: \"%.*s\"\n", l, buffer(start+c+ovector[0]));
|
||||
s.replace(r, 1, buffer(start+c+ovector[0]), l);
|
||||
r += l - 1;
|
||||
}
|
||||
else continue;
|
||||
debug("subs = \"%s\"\n", s());
|
||||
}
|
||||
buffer.replace(start+c+ovector[0], l, s);
|
||||
length += s.length() - l;
|
||||
c += s.length();
|
||||
if (n == fmt.prec) // max match reached
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int RegexpConverter::
|
||||
scanPseudo(const StreamFormat& fmt, StreamBuffer& input, long& cursor)
|
||||
{
|
||||
/* re-write input buffer */
|
||||
regsubst(fmt, input, cursor);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool RegexpConverter::
|
||||
printPseudo(const StreamFormat& fmt, StreamBuffer& output)
|
||||
{
|
||||
/* re-write output buffer */
|
||||
regsubst(fmt, output, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
RegisterConverter (RegexpConverter, "/");
|
||||
|
@ -26,8 +26,7 @@
|
||||
|
||||
#if defined(__vxworks) || defined(vxWorks) || defined(_WIN32) || defined(__rtems__)
|
||||
// These systems have no vsnprintf
|
||||
#include <epicsStdio.h>
|
||||
#define vsnprintf epicsVsnprintf
|
||||
#define vsnprintf(p,l,f,v) vsprintf(p,f,v)
|
||||
#endif
|
||||
|
||||
#define P PRINTF_SIZE_T_PREFIX
|
||||
@ -296,7 +295,7 @@ StreamBuffer StreamBuffer::expand(ssize_t start, ssize_t length) const
|
||||
for (i = start; i < end; i++)
|
||||
{
|
||||
c = buffer[i];
|
||||
if ((c & 0x7f) < 0x20 || (c & 0x7f) == 0x7f)
|
||||
if (c < 0x20 || c >= 0x7f)
|
||||
{
|
||||
result.print("<%02x>", c & 0xff);
|
||||
}
|
||||
|
@ -23,6 +23,8 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifndef __GNUC__
|
||||
#define __attribute__(x)
|
||||
#endif
|
||||
|
@ -460,7 +460,7 @@ finishProtocol(ProtocolResult status)
|
||||
status = Fault;
|
||||
}
|
||||
//// flags &= ~(AcceptInput|AcceptEvent);
|
||||
if (runningHandler)
|
||||
if (runningHandler || flags & InitRun)
|
||||
{
|
||||
// get original error status
|
||||
if (status == Success) status = runningHandler;
|
||||
@ -749,7 +749,7 @@ printSeparator()
|
||||
bool StreamCore::
|
||||
printValue(const StreamFormat& fmt, long value)
|
||||
{
|
||||
if (fmt.type != long_format && fmt.type != enum_format)
|
||||
if (fmt.type != unsigned_format && fmt.type != signed_format && fmt.type != enum_format)
|
||||
{
|
||||
error("%s: printValue(long) called with %%%c format\n",
|
||||
name(), fmt.conv);
|
||||
@ -832,7 +832,7 @@ lockCallback(StreamIoStatus status)
|
||||
case StreamIoSuccess:
|
||||
break;
|
||||
case StreamIoTimeout:
|
||||
error("%s: Cannot lock device within %ld ms, device seems to be busy\n",
|
||||
debug("%s: Cannot lock device within %ld ms, device seems to be busy\n",
|
||||
name(), lockTimeout);
|
||||
flags &= ~BusOwner;
|
||||
finishProtocol(LockTimeout);
|
||||
@ -991,7 +991,7 @@ readCallback(StreamIoStatus status,
|
||||
evalIn();
|
||||
return 0;
|
||||
}
|
||||
error("%s: No reply from device within %ld ms\n",
|
||||
debug("StreamCore::readCallback(%s): No reply from device within %ld ms\n",
|
||||
name(), replyTimeout);
|
||||
inputBuffer.clear();
|
||||
finishProtocol(ReplyTimeout);
|
||||
@ -1216,7 +1216,8 @@ normal_format:
|
||||
double ddummy;
|
||||
switch (fmt.type)
|
||||
{
|
||||
case long_format:
|
||||
case unsigned_format:
|
||||
case signed_format:
|
||||
case enum_format:
|
||||
consumed = StreamFormatConverter::find(fmt.conv)->
|
||||
scanLong(fmt, inputLine(consumedInput), ldummy);
|
||||
@ -1457,7 +1458,7 @@ matchSeparator()
|
||||
long StreamCore::
|
||||
scanValue(const StreamFormat& fmt, long& value)
|
||||
{
|
||||
if (fmt.type != long_format && fmt.type != enum_format)
|
||||
if (fmt.type != unsigned_format && fmt.type != signed_format && fmt.type != enum_format)
|
||||
{
|
||||
error("%s: scanValue(long&) called with %%%c format\n",
|
||||
name(), fmt.conv);
|
||||
@ -1478,6 +1479,7 @@ scanValue(const StreamFormat& fmt, long& value)
|
||||
}
|
||||
else return -1;
|
||||
}
|
||||
if (fmt.flags & fix_width_flag && consumed != fmt.width) return -1;
|
||||
if (consumed > inputLine.length()-consumedInput) return -1;
|
||||
debug("StreamCore::scanValue(%s) scanned %li\n",
|
||||
name(), value);
|
||||
@ -1509,6 +1511,7 @@ scanValue(const StreamFormat& fmt, double& value)
|
||||
}
|
||||
else return -1;
|
||||
}
|
||||
if (fmt.flags & fix_width_flag && (consumed != (fmt.width + fmt.prec + 1))) return -1;
|
||||
if (consumed > inputLine.length()-consumedInput) return -1;
|
||||
debug("StreamCore::scanValue(%s) scanned %#g\n",
|
||||
name(), value);
|
||||
@ -1541,6 +1544,7 @@ scanValue(const StreamFormat& fmt, char* value, long maxlen)
|
||||
}
|
||||
else return -1;
|
||||
}
|
||||
if (fmt.flags & fix_width_flag && consumed != fmt.width) return -1;
|
||||
if (consumed > inputLine.length()-consumedInput) return -1;
|
||||
#ifndef NO_TEMPORARY
|
||||
debug("StreamCore::scanValue(%s) scanned \"%s\"\n",
|
||||
@ -1653,7 +1657,11 @@ timerCallback()
|
||||
bool StreamCore::
|
||||
evalExec()
|
||||
{
|
||||
formatOutput();
|
||||
if (!formatOutput())
|
||||
{
|
||||
finishProtocol(FormatError);
|
||||
return false;
|
||||
}
|
||||
debug ("StreamCore::evalExec: command = \"%s\"\n", outputLine.expand()());
|
||||
// release bus
|
||||
if (flags & BusOwner)
|
||||
|
@ -115,7 +115,6 @@ class Stream : protected StreamCore
|
||||
epicsMutex mutex;
|
||||
epicsEvent initDone;
|
||||
#endif
|
||||
StreamBuffer fieldBuffer;
|
||||
int status;
|
||||
int convert;
|
||||
long currentValueLength;
|
||||
@ -181,6 +180,7 @@ public:
|
||||
#ifndef EPICS_3_13
|
||||
extern "C" {
|
||||
epicsExportAddress(int, streamDebug);
|
||||
epicsExportAddress(int, streamError);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -604,7 +604,7 @@ parseLink(const struct link *ioLink, char* filename,
|
||||
if (items <= 0)
|
||||
{
|
||||
error("%s: Empty I/O link. "
|
||||
"Forgot the leading '@' or confused INP with OUT ?\n",
|
||||
"Forgot the leading '@' or confused INP with OUT or link is too long ?\n",
|
||||
name());
|
||||
return S_dev_badInitRet;
|
||||
}
|
||||
@ -751,21 +751,16 @@ process()
|
||||
bool Stream::
|
||||
print(format_t *format, va_list ap)
|
||||
{
|
||||
long lval;
|
||||
double dval;
|
||||
char* sval;
|
||||
switch (format->type)
|
||||
{
|
||||
case DBF_ENUM:
|
||||
case DBF_ULONG:
|
||||
case DBF_LONG:
|
||||
lval = va_arg(ap, long);
|
||||
return printValue(*format->priv, lval);
|
||||
case DBF_ENUM:
|
||||
return printValue(*format->priv, va_arg(ap, long));
|
||||
case DBF_DOUBLE:
|
||||
dval = va_arg(ap, double);
|
||||
return printValue(*format->priv, dval);
|
||||
return printValue(*format->priv, va_arg(ap, double));
|
||||
case DBF_STRING:
|
||||
sval = va_arg(ap, char*);
|
||||
return printValue(*format->priv, sval);
|
||||
return printValue(*format->priv, va_arg(ap, char*));
|
||||
}
|
||||
error("INTERNAL ERROR (%s): Illegal format type\n", name());
|
||||
return false;
|
||||
@ -775,27 +770,22 @@ bool Stream::
|
||||
scan(format_t *format, void* value, size_t maxStringSize)
|
||||
{
|
||||
// called by streamScanfN
|
||||
long* lptr;
|
||||
double* dptr;
|
||||
char* sptr;
|
||||
|
||||
// first remove old value from inputLine (if we are scanning arrays)
|
||||
consumedInput += currentValueLength;
|
||||
currentValueLength = 0;
|
||||
switch (format->type)
|
||||
{
|
||||
case DBF_ULONG:
|
||||
case DBF_LONG:
|
||||
case DBF_ENUM:
|
||||
lptr = (long*)value;
|
||||
currentValueLength = scanValue(*format->priv, *lptr);
|
||||
currentValueLength = scanValue(*format->priv, *(long*)value);
|
||||
break;
|
||||
case DBF_DOUBLE:
|
||||
dptr = (double*)value;
|
||||
currentValueLength = scanValue(*format->priv, *dptr);
|
||||
currentValueLength = scanValue(*format->priv, *(double*)value);
|
||||
break;
|
||||
case DBF_STRING:
|
||||
sptr = (char*)value;
|
||||
currentValueLength = scanValue(*format->priv, sptr,
|
||||
currentValueLength = scanValue(*format->priv, (char*)value,
|
||||
maxStringSize);
|
||||
break;
|
||||
default:
|
||||
@ -979,9 +969,9 @@ getFieldAddress(const char* fieldname, StreamBuffer& address)
|
||||
}
|
||||
|
||||
static const unsigned char dbfMapping[] =
|
||||
{0, DBF_LONG, DBF_ENUM, DBF_DOUBLE, DBF_STRING};
|
||||
{0, DBF_ULONG, DBF_LONG, DBF_ENUM, DBF_DOUBLE, DBF_STRING};
|
||||
static const short typeSize[] =
|
||||
{0, sizeof(epicsInt32), sizeof(epicsUInt16),
|
||||
{0, sizeof(epicsUInt32), sizeof(epicsInt32), sizeof(epicsUInt16),
|
||||
sizeof(epicsFloat64), MAX_STRING_SIZE};
|
||||
|
||||
bool Stream::
|
||||
@ -993,10 +983,14 @@ formatValue(const StreamFormat& format, const void* fieldaddress)
|
||||
// -- TO DO: If SCAN is "I/O Intr" and record has not been processed, --
|
||||
// -- do it now to get the latest value (only for output records?) --
|
||||
|
||||
format_s fmt;
|
||||
fmt.type = dbfMapping[format.type];
|
||||
fmt.priv = &format;
|
||||
if (fieldaddress)
|
||||
{
|
||||
// Format like "%([record.]field)..." has requested to get value
|
||||
// from field of this or other record.
|
||||
StreamBuffer fieldBuffer;
|
||||
DBADDR* pdbaddr = (DBADDR*)fieldaddress;
|
||||
|
||||
/* Handle time stamps special. %T converter takes double. */
|
||||
@ -1025,7 +1019,6 @@ formatValue(const StreamFormat& format, const void* fieldaddress)
|
||||
}
|
||||
|
||||
/* convert type to LONG, ENUM, DOUBLE, or STRING */
|
||||
int type = dbfMapping[format.type];
|
||||
long nelem = pdbaddr->no_elements;
|
||||
size_t size = nelem * typeSize[format.type];
|
||||
|
||||
@ -1040,13 +1033,13 @@ formatValue(const StreamFormat& format, const void* fieldaddress)
|
||||
nelem,
|
||||
pdbaddr->field_size,
|
||||
pamapdbfType[pdbaddr->field_type].strvalue);
|
||||
type = DBF_CHAR;
|
||||
fmt.type = DBF_CHAR;
|
||||
size = nelem;
|
||||
}
|
||||
|
||||
char* buffer = fieldBuffer.clear().reserve(size);
|
||||
|
||||
if (dbGet(pdbaddr, type, buffer,
|
||||
if (dbGet(pdbaddr, fmt.type, buffer,
|
||||
NULL, &nelem, NULL) != 0)
|
||||
{
|
||||
error("%s: dbGet(%s.%s, %s) failed\n",
|
||||
@ -1060,7 +1053,7 @@ formatValue(const StreamFormat& format, const void* fieldaddress)
|
||||
name(),nelem);
|
||||
|
||||
/* terminate CHAR array as string */
|
||||
if (type == DBF_CHAR)
|
||||
if (fmt.type == DBF_CHAR)
|
||||
{
|
||||
if (nelem >= pdbaddr->no_elements) nelem = pdbaddr->no_elements-1;
|
||||
buffer[nelem] = 0;
|
||||
@ -1077,7 +1070,12 @@ formatValue(const StreamFormat& format, const void* fieldaddress)
|
||||
(long)((epicsUInt16*)buffer)[i]))
|
||||
return false;
|
||||
break;
|
||||
case long_format:
|
||||
case unsigned_format:
|
||||
if (!printValue(format,
|
||||
(long)((epicsUInt32*)buffer)[i]))
|
||||
return false;
|
||||
break;
|
||||
case signed_format:
|
||||
if (!printValue(format,
|
||||
(long)((epicsInt32*)buffer)[i]))
|
||||
return false;
|
||||
@ -1104,9 +1102,6 @@ formatValue(const StreamFormat& format, const void* fieldaddress)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
format_s fmt;
|
||||
fmt.type = dbfMapping[format.type];
|
||||
fmt.priv = &format;
|
||||
debug("Stream::formatValue(%s) format=%%%c type=%s\n",
|
||||
name(), format.conv, pamapdbfType[fmt.type].strvalue);
|
||||
if (!writeData)
|
||||
@ -1126,17 +1121,21 @@ bool Stream::
|
||||
matchValue(const StreamFormat& format, const void* fieldaddress)
|
||||
{
|
||||
// this function must increase consumedInput
|
||||
long consumed;
|
||||
long consumed = 0;
|
||||
long lval;
|
||||
double dval;
|
||||
char* buffer;
|
||||
int status;
|
||||
const char* putfunc;
|
||||
format_s fmt;
|
||||
|
||||
fmt.type = dbfMapping[format.type];
|
||||
fmt.priv = &format;
|
||||
if (fieldaddress)
|
||||
{
|
||||
// Format like "%([record.]field)..." has requested to put value
|
||||
// to field of this or other record.
|
||||
StreamBuffer fieldBuffer;
|
||||
DBADDR* pdbaddr = (DBADDR*)fieldaddress;
|
||||
long nord;
|
||||
long nelem = pdbaddr->no_elements;
|
||||
@ -1148,12 +1147,24 @@ matchValue(const StreamFormat& format, const void* fieldaddress)
|
||||
name(), fieldBuffer.expand()());
|
||||
switch (format.type)
|
||||
{
|
||||
case long_format:
|
||||
case unsigned_format:
|
||||
{
|
||||
consumed = scanValue(format, lval);
|
||||
if (consumed >= 0) ((epicsUInt32*)buffer)[nord] = lval;
|
||||
debug("Stream::matchValue(%s): %s.%s[%li] = %lu\n",
|
||||
name(), pdbaddr->precord->name,
|
||||
((dbFldDes*)pdbaddr->pfldDes)->name,
|
||||
nord, lval);
|
||||
break;
|
||||
}
|
||||
case signed_format:
|
||||
{
|
||||
consumed = scanValue(format, lval);
|
||||
if (consumed >= 0) ((epicsInt32*)buffer)[nord] = lval;
|
||||
debug("Stream::matchValue(%s): %s[%li] = %li\n",
|
||||
name(), pdbaddr->precord->name, nord, lval);
|
||||
debug("Stream::matchValue(%s): %s.%s[%li] = %li\n",
|
||||
name(), pdbaddr->precord->name,
|
||||
((dbFldDes*)pdbaddr->pfldDes)->name,
|
||||
nord, lval);
|
||||
break;
|
||||
}
|
||||
case enum_format:
|
||||
@ -1161,8 +1172,10 @@ matchValue(const StreamFormat& format, const void* fieldaddress)
|
||||
consumed = scanValue(format, lval);
|
||||
if (consumed >= 0)
|
||||
((epicsUInt16*)buffer)[nord] = (epicsUInt16)lval;
|
||||
debug("Stream::matchValue(%s): %s[%li] = %li\n",
|
||||
name(), pdbaddr->precord->name, nord, lval);
|
||||
debug("Stream::matchValue(%s): %s.%s[%li] = %li\n",
|
||||
name(), pdbaddr->precord->name,
|
||||
((dbFldDes*)pdbaddr->pfldDes)->name,
|
||||
nord, lval);
|
||||
break;
|
||||
}
|
||||
case double_format:
|
||||
@ -1175,18 +1188,34 @@ matchValue(const StreamFormat& format, const void* fieldaddress)
|
||||
if (consumed >= 0)
|
||||
memcpy(((epicsFloat64*)buffer)+nord,
|
||||
&f64, sizeof(f64));
|
||||
debug("Stream::matchValue(%s): %s[%li] = %#g %#g\n",
|
||||
name(), pdbaddr->precord->name, nord, dval,
|
||||
debug("Stream::matchValue(%s): %s.%s[%li] = %#g %#g\n",
|
||||
name(), pdbaddr->precord->name,
|
||||
((dbFldDes*)pdbaddr->pfldDes)->name,
|
||||
nord, dval,
|
||||
((epicsFloat64*)buffer)[nord]);
|
||||
break;
|
||||
}
|
||||
case string_format:
|
||||
{
|
||||
if (pdbaddr->field_type == DBF_CHAR)
|
||||
{
|
||||
// string to char array
|
||||
consumed = scanValue(format, buffer, nelem);
|
||||
debug("Stream::matchValue(%s): %s.%s = \"%.*s\"\n",
|
||||
name(), pdbaddr->precord->name,
|
||||
((dbFldDes*)pdbaddr->pfldDes)->name,
|
||||
(int)consumed, buffer);
|
||||
nord = nelem;
|
||||
}
|
||||
else
|
||||
{
|
||||
consumed = scanValue(format,
|
||||
buffer+MAX_STRING_SIZE*nord, MAX_STRING_SIZE);
|
||||
debug("Stream::matchValue(%s): %s[%li] = \"%.*s\"\n",
|
||||
name(), pdbaddr->precord->name, nord,
|
||||
MAX_STRING_SIZE, buffer+MAX_STRING_SIZE*nord);
|
||||
debug("Stream::matchValue(%s): %s.%s[%li] = \"%.*s\"\n",
|
||||
name(), pdbaddr->precord->name,
|
||||
((dbFldDes*)pdbaddr->pfldDes)->name,
|
||||
nord, MAX_STRING_SIZE, buffer+MAX_STRING_SIZE*nord);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -1223,15 +1252,17 @@ matchValue(const StreamFormat& format, const void* fieldaddress)
|
||||
name());
|
||||
return false;
|
||||
}
|
||||
/* convert from Unix epoch (1 Jan 1970) to EPICS epoch (1 Jan 1990) */
|
||||
dval = dval-631152000u;
|
||||
pdbaddr->precord->time.secPastEpoch = (long)dval;
|
||||
// rouding: we don't have 9 digits precision
|
||||
// in a double of today's number of seconds
|
||||
pdbaddr->precord->time.nsec = (long)((dval-(long)dval)*1e6)*1000;
|
||||
debug("Stream::matchValue(%s): writing %i.%i to TIME field\n",
|
||||
debug("Stream::matchValue(%s): writing %i.%i to %s.TIME field\n",
|
||||
name(),
|
||||
pdbaddr->precord->time.secPastEpoch,
|
||||
pdbaddr->precord->time.nsec);
|
||||
pdbaddr->precord->time.nsec,
|
||||
pdbaddr->precord->name);
|
||||
pdbaddr->precord->tse = epicsTimeEventDeviceTime;
|
||||
return true;
|
||||
#else
|
||||
@ -1240,18 +1271,19 @@ matchValue(const StreamFormat& format, const void* fieldaddress)
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (format.type == string_format &&
|
||||
(pdbaddr->field_type == DBF_CHAR || pdbaddr->field_type == DBF_UCHAR))
|
||||
{
|
||||
/* write strings to [U]CHAR arrays */
|
||||
nord = consumed;
|
||||
fmt.type = DBF_CHAR;
|
||||
}
|
||||
if (pdbaddr->precord == record || INIT_RUN)
|
||||
{
|
||||
// write into own record, thus don't process it
|
||||
// in @init we must not process other record
|
||||
debug("Stream::matchValue(%s): dbPut(%s.%s,%s)\n",
|
||||
name(),
|
||||
pdbaddr->precord->name,
|
||||
((dbFldDes*)pdbaddr->pfldDes)->name,
|
||||
fieldBuffer.expand()());
|
||||
putfunc = "dbPut";
|
||||
status = dbPut(pdbaddr, dbfMapping[format.type], buffer, nord);
|
||||
status = dbPut(pdbaddr, fmt.type, buffer, nord);
|
||||
if (INIT_RUN && pdbaddr->precord != record)
|
||||
{
|
||||
// clean error status of other record in @init
|
||||
@ -1263,41 +1295,44 @@ matchValue(const StreamFormat& format, const void* fieldaddress)
|
||||
else
|
||||
{
|
||||
// write into other record, thus process it
|
||||
debug("Stream::matchValue(%s): dbPutField(%s.%s,%s)\n",
|
||||
name(),
|
||||
pdbaddr->precord->name,
|
||||
((dbFldDes*)pdbaddr->pfldDes)->name,
|
||||
fieldBuffer.expand()());
|
||||
putfunc = "dbPutField";
|
||||
status = dbPutField(pdbaddr, dbfMapping[format.type],
|
||||
status = dbPutField(pdbaddr, fmt.type,
|
||||
buffer, nord);
|
||||
}
|
||||
debug("Stream::matchValue(%s): %s(%s.%s, %s, %s) status=0x%x\n",
|
||||
name(), putfunc,
|
||||
pdbaddr->precord->name,
|
||||
((dbFldDes*)pdbaddr->pfldDes)->name,
|
||||
pamapdbfType[fmt.type].strvalue,
|
||||
fieldBuffer.expand()(),
|
||||
status);
|
||||
if (status != 0)
|
||||
{
|
||||
flags &= ~ScanTried;
|
||||
switch (format.type)
|
||||
switch (fmt.type)
|
||||
{
|
||||
case long_format:
|
||||
case enum_format:
|
||||
case DBF_ULONG:
|
||||
case DBF_LONG:
|
||||
case DBF_ENUM:
|
||||
error("%s: %s(%s.%s, %s, %li) failed\n",
|
||||
putfunc, name(), pdbaddr->precord->name,
|
||||
name(), putfunc, pdbaddr->precord->name,
|
||||
((dbFldDes*)pdbaddr->pfldDes)->name,
|
||||
pamapdbfType[dbfMapping[format.type]].strvalue,
|
||||
pamapdbfType[fmt.type].strvalue,
|
||||
lval);
|
||||
return false;
|
||||
case double_format:
|
||||
case DBF_DOUBLE:
|
||||
error("%s: %s(%s.%s, %s, %#g) failed\n",
|
||||
putfunc, name(), pdbaddr->precord->name,
|
||||
name(), putfunc, pdbaddr->precord->name,
|
||||
((dbFldDes*)pdbaddr->pfldDes)->name,
|
||||
pamapdbfType[dbfMapping[format.type]].strvalue,
|
||||
pamapdbfType[fmt.type].strvalue,
|
||||
dval);
|
||||
return false;
|
||||
case string_format:
|
||||
error("%s: %s(%s.%s, %s, \"%s\") failed\n",
|
||||
putfunc, name(), pdbaddr->precord->name,
|
||||
case DBF_STRING:
|
||||
error("%s: %s(%s.%s, %s, \"%.*s\") failed\n",
|
||||
name(), putfunc, pdbaddr->precord->name,
|
||||
((dbFldDes*)pdbaddr->pfldDes)->name,
|
||||
pamapdbfType[dbfMapping[format.type]].strvalue,
|
||||
buffer);
|
||||
pamapdbfType[fmt.type].strvalue,
|
||||
(int)consumed, buffer);
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
@ -1306,9 +1341,6 @@ matchValue(const StreamFormat& format, const void* fieldaddress)
|
||||
return true;
|
||||
}
|
||||
// no fieldaddress (the "normal" case)
|
||||
format_s fmt;
|
||||
fmt.type = dbfMapping[format.type];
|
||||
fmt.priv = &format;
|
||||
if (!readData)
|
||||
{
|
||||
error("%s: No readData() function provided\n", name());
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int streamDebug = 0;
|
||||
int streamError = 0;
|
||||
extern "C" {
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
@ -74,6 +75,7 @@ void StreamError(int line, const char* file, const char* fmt, ...)
|
||||
void StreamVError(int line, const char* file, const char* fmt, va_list args)
|
||||
{
|
||||
char timestamp[40];
|
||||
if (!streamError) return; // Error logging disabled
|
||||
StreamPrintTimestampFunction(timestamp, 40);
|
||||
#ifdef va_copy
|
||||
if (StreamDebugFile)
|
||||
|
@ -28,6 +28,7 @@
|
||||
#endif
|
||||
|
||||
extern int streamDebug;
|
||||
extern int streamError;
|
||||
extern void (*StreamPrintTimestampFunction)(char* buffer, int size);
|
||||
|
||||
void StreamError(int line, const char* file, const char* fmt, ...)
|
||||
|
@ -30,15 +30,17 @@ typedef enum {
|
||||
zero_flag = 0x10,
|
||||
skip_flag = 0x20,
|
||||
default_flag = 0x40,
|
||||
compare_flag = 0x80
|
||||
compare_flag = 0x80,
|
||||
fix_width_flag = 0x100
|
||||
} StreamFormatFlag;
|
||||
|
||||
typedef enum {
|
||||
long_format = 1,
|
||||
enum_format = 2,
|
||||
double_format = 3,
|
||||
string_format = 4,
|
||||
pseudo_format = 5
|
||||
unsigned_format = 1,
|
||||
signed_format,
|
||||
enum_format,
|
||||
double_format,
|
||||
string_format,
|
||||
pseudo_format
|
||||
} StreamFormatType;
|
||||
|
||||
extern const char* StreamFormatTypeStr[];
|
||||
@ -47,7 +49,7 @@ typedef struct StreamFormat
|
||||
{
|
||||
char conv;
|
||||
StreamFormatType type;
|
||||
unsigned char flags;
|
||||
unsigned short flags;
|
||||
short prec;
|
||||
unsigned short width;
|
||||
unsigned short infolen;
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include "StreamFormatConverter.h"
|
||||
#include "StreamError.h"
|
||||
|
||||
@ -37,7 +38,7 @@ parseFormat(const char*& source, FormatType formatType, StreamFormat& streamForm
|
||||
{
|
||||
/*
|
||||
source := [flags] [width] ['.' prec] conv [extra]
|
||||
flags := '-' | '+' | ' ' | '#' | '0' | '*' | '?' | '='
|
||||
flags := '-' | '+' | ' ' | '#' | '0' | '*' | '?' | '=' | '!'
|
||||
width := integer
|
||||
prec := integer
|
||||
conv := character
|
||||
@ -84,6 +85,15 @@ parseFormat(const char*& source, FormatType formatType, StreamFormat& streamForm
|
||||
}
|
||||
streamFormat.flags |= default_flag;
|
||||
break;
|
||||
case '!':
|
||||
if (formatType != ScanFormat)
|
||||
{
|
||||
error("Use of fixed width modifier '!' "
|
||||
"only allowed in input formats\n");
|
||||
return false;
|
||||
}
|
||||
streamFormat.flags |= fix_width_flag;
|
||||
break;
|
||||
case '=':
|
||||
if (formatType != ScanFormat)
|
||||
{
|
||||
@ -310,12 +320,16 @@ parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
info.append('l');
|
||||
info.append(fmt.conv);
|
||||
}
|
||||
return long_format;
|
||||
if (fmt.conv == 'd' || fmt.conv == 'i') return signed_format;
|
||||
return unsigned_format;
|
||||
}
|
||||
|
||||
bool StdLongConverter::
|
||||
printLong(const StreamFormat& fmt, StreamBuffer& output, long value)
|
||||
{
|
||||
// limits %x/%X formats to number of half bytes in width.
|
||||
if (fmt.width && (fmt.conv == 'x' || fmt.conv == 'X') && fmt.width < 2*sizeof(long))
|
||||
value &= ~(-1L << (fmt.width*4));
|
||||
output.print(fmt.info, value);
|
||||
return true;
|
||||
}
|
||||
@ -327,6 +341,7 @@ scanLong(const StreamFormat& fmt, const char* input, long& value)
|
||||
int length;
|
||||
bool neg;
|
||||
int base;
|
||||
long v;
|
||||
|
||||
length = prepareval(fmt, input, neg);
|
||||
if (length < 0) return -1;
|
||||
@ -336,14 +351,11 @@ scanLong(const StreamFormat& fmt, const char* input, long& value)
|
||||
base = 10;
|
||||
break;
|
||||
case 'o':
|
||||
base = 8;
|
||||
goto signcheck;
|
||||
case 'x':
|
||||
case 'X':
|
||||
base = 16;
|
||||
signcheck:
|
||||
// allow negative hex and oct numbers with - flag
|
||||
if (neg && !(fmt.flags & left_flag)) return -1;
|
||||
base = (fmt.conv == 'o') ? 8 : 16;
|
||||
break;
|
||||
case 'u':
|
||||
if (neg) return -1;
|
||||
@ -352,10 +364,10 @@ signcheck:
|
||||
default:
|
||||
base = 0;
|
||||
}
|
||||
value = strtoul(input, &end, base);
|
||||
if (neg) value = -value;
|
||||
v = strtoul(input, &end, base);
|
||||
if (end == input) return -1;
|
||||
length += end-input;
|
||||
value = neg ? -v : v;
|
||||
return length;
|
||||
}
|
||||
|
||||
@ -430,10 +442,11 @@ int StdStringConverter::
|
||||
parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
const char*& source, bool scanFormat)
|
||||
{
|
||||
if (fmt.flags & (sign_flag|zero_flag))
|
||||
if (fmt.flags & sign_flag)
|
||||
{
|
||||
error("Use of modifiers '+', '0'"
|
||||
"not allowed with %%s conversion\n");
|
||||
error("Use of modifier '+'"
|
||||
"not allowed with %%%c conversion\n",
|
||||
fmt.conv);
|
||||
return false;
|
||||
}
|
||||
if (scanFormat && fmt.prec >= 0)
|
||||
@ -451,6 +464,21 @@ parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
bool StdStringConverter::
|
||||
printString(const StreamFormat& fmt, StreamBuffer& output, const char* value)
|
||||
{
|
||||
if (fmt.flags & zero_flag && fmt.width)
|
||||
{
|
||||
size_t l;
|
||||
if (fmt.prec > -1)
|
||||
{
|
||||
char* p = (char*)memchr(value, 0, fmt.prec);
|
||||
if (p) l = p - value;
|
||||
else l = fmt.prec;
|
||||
}
|
||||
else l = strlen(value);
|
||||
if (!(fmt.flags & left_flag)) output.append('\0', fmt.width-l);
|
||||
output.append(value, l);
|
||||
if (fmt.flags & left_flag) output.append('\0', fmt.width-l);
|
||||
}
|
||||
else
|
||||
output.print(fmt.info, value);
|
||||
return true;
|
||||
}
|
||||
@ -543,7 +571,7 @@ parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
info.append("%n");
|
||||
return string_format;
|
||||
}
|
||||
return long_format;
|
||||
return unsigned_format;
|
||||
}
|
||||
|
||||
bool StdCharsConverter::
|
||||
|
@ -26,7 +26,8 @@
|
||||
#include "StreamError.h"
|
||||
|
||||
const char* StreamFormatTypeStr[] = {
|
||||
"none", "long", "enum", "double", "string", "pseudo"
|
||||
// must match the order in StreamFormat.h
|
||||
"none", "unsigned", "signed", "enum", "double", "string", "pseudo"
|
||||
};
|
||||
|
||||
class StreamProtocolParser::Protocol::Variable
|
||||
@ -1438,7 +1439,7 @@ compileFormat(StreamBuffer& buffer, const char*& formatstr,
|
||||
// parsing failed
|
||||
return false;
|
||||
}
|
||||
if (type < long_format && type > pseudo_format)
|
||||
if (type < 1 && type > pseudo_format)
|
||||
{
|
||||
error(line, filename(),
|
||||
"Illegal format type %d returned from '%%%c' converter\n",
|
||||
|
@ -22,8 +22,8 @@
|
||||
#define devStream_h
|
||||
|
||||
#define STREAM_MAJOR 2
|
||||
#define STREAM_MINOR 6
|
||||
#define STREAM_PATCHLEVEL 6
|
||||
#define STREAM_MINOR 8
|
||||
#define STREAM_PATCHLEVEL 0
|
||||
|
||||
#if defined(__vxworks) || defined(vxWorks)
|
||||
#include <vxWorks.h>
|
||||
@ -58,7 +58,6 @@ extern "C" {
|
||||
#include <dbCommon.h>
|
||||
#include <dbScan.h>
|
||||
#include <devSup.h>
|
||||
/* #include <dbFldTypes.h> */
|
||||
#include <dbAccess.h>
|
||||
|
||||
#ifdef devStream_epicsExportSharedSymbols
|
||||
|
@ -20,9 +20,9 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "devStream.h"
|
||||
#include <aaiRecord.h>
|
||||
#include <errlog.h>
|
||||
#include <aaiRecord.h>
|
||||
#include "devStream.h"
|
||||
#include <epicsExport.h>
|
||||
|
||||
static long readData (dbCommon *record, format_t *format)
|
||||
@ -57,6 +57,7 @@ static long readData (dbCommon *record, format_t *format)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DBF_ULONG:
|
||||
case DBF_LONG:
|
||||
case DBF_ENUM:
|
||||
{
|
||||
@ -191,6 +192,7 @@ static long writeData (dbCommon *record, format_t *format)
|
||||
return ERROR;
|
||||
break;
|
||||
}
|
||||
case DBF_ULONG:
|
||||
case DBF_LONG:
|
||||
case DBF_ENUM:
|
||||
{
|
||||
|
@ -20,9 +20,9 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "devStream.h"
|
||||
#include <aaoRecord.h>
|
||||
#include <errlog.h>
|
||||
#include <aaoRecord.h>
|
||||
#include "devStream.h"
|
||||
#include <epicsExport.h>
|
||||
|
||||
static long readData (dbCommon *record, format_t *format)
|
||||
@ -57,6 +57,7 @@ static long readData (dbCommon *record, format_t *format)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DBF_ULONG:
|
||||
case DBF_LONG:
|
||||
case DBF_ENUM:
|
||||
{
|
||||
@ -191,6 +192,7 @@ static long writeData (dbCommon *record, format_t *format)
|
||||
return ERROR;
|
||||
break;
|
||||
}
|
||||
case DBF_ULONG:
|
||||
case DBF_LONG:
|
||||
case DBF_ENUM:
|
||||
{
|
||||
|
@ -18,55 +18,86 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include "devStream.h"
|
||||
#include <aiRecord.h>
|
||||
#include "devStream.h"
|
||||
#ifdef EPICS_3_13
|
||||
#include <private/mathP.h>
|
||||
#define isinf(x) isInf(x)
|
||||
#define isnan(x) isNan(x)
|
||||
#else
|
||||
#include <epicsMath.h>
|
||||
#endif
|
||||
#include <epicsExport.h>
|
||||
|
||||
static long readData (dbCommon *record, format_t *format)
|
||||
{
|
||||
aiRecord *ai = (aiRecord *) record;
|
||||
double val;
|
||||
|
||||
switch (format->type)
|
||||
{
|
||||
case DBF_DOUBLE:
|
||||
{
|
||||
double val;
|
||||
if (streamScanf (record, format, &val)) return ERROR;
|
||||
if (ai->aslo != 0.0) val *= ai->aslo;
|
||||
val += ai->aoff;
|
||||
if (!INIT_RUN && ai->smoo != 0.0)
|
||||
{
|
||||
val = ai->val * ai->smoo + val * (1.0 - ai->smoo);
|
||||
}
|
||||
ai->val = val;
|
||||
return DO_NOT_CONVERT;
|
||||
break;
|
||||
}
|
||||
case DBF_ULONG:
|
||||
case DBF_LONG:
|
||||
{
|
||||
long rval;
|
||||
if (streamScanf (record, format, &rval)) return ERROR;
|
||||
ai->rval = rval;
|
||||
if (ai->linr == 0)
|
||||
{
|
||||
/* allow integers with more than 32 bits */
|
||||
if (format->type == DBF_ULONG)
|
||||
val = (unsigned long)rval;
|
||||
else
|
||||
val = rval;
|
||||
break;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
default:
|
||||
return ERROR;
|
||||
}
|
||||
if (ai->aslo != 0.0 && ai->aslo != 1.0) val *= ai->aslo;
|
||||
val += ai->aoff;
|
||||
if (!(ai->smoo == 0.0 || ai->init || ai->udf || isinf(ai->val) || isnan(ai->val)))
|
||||
val = ai->val * ai->smoo + val * (1.0 - ai->smoo);
|
||||
ai->val = val;
|
||||
return DO_NOT_CONVERT;
|
||||
}
|
||||
|
||||
static long writeData (dbCommon *record, format_t *format)
|
||||
{
|
||||
aiRecord *ai = (aiRecord *) record;
|
||||
double val;
|
||||
|
||||
double val = ai->val - ai->aoff;
|
||||
if (ai->aslo != 0.0 && ai->aslo != 1.0) val /= ai->aslo;
|
||||
|
||||
switch (format->type)
|
||||
{
|
||||
case DBF_DOUBLE:
|
||||
{
|
||||
val = ai->val - ai->aoff;
|
||||
if (ai->aslo != 0) val /= ai->aslo;
|
||||
return streamPrintf (record, format, val);
|
||||
}
|
||||
case DBF_ULONG:
|
||||
{
|
||||
if (ai->linr == 0)
|
||||
{
|
||||
/* allow more bits than 32 */
|
||||
return streamPrintf (record, format, (unsigned long)val);
|
||||
}
|
||||
return streamPrintf (record, format, (unsigned long)ai->rval);
|
||||
}
|
||||
case DBF_LONG:
|
||||
{
|
||||
if (ai->linr == 0)
|
||||
{
|
||||
/* allow more bits than 32 */
|
||||
return streamPrintf (record, format, (long)val);
|
||||
}
|
||||
return streamPrintf (record, format, (long)ai->rval);
|
||||
}
|
||||
}
|
||||
|
@ -18,53 +18,77 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include "devStream.h"
|
||||
#include <aoRecord.h>
|
||||
#include "devStream.h"
|
||||
#include <epicsExport.h>
|
||||
|
||||
static long readData (dbCommon *record, format_t *format)
|
||||
{
|
||||
aoRecord *ao = (aoRecord *) record;
|
||||
double val;
|
||||
|
||||
switch (format->type)
|
||||
{
|
||||
case DBF_DOUBLE:
|
||||
{
|
||||
double val;
|
||||
if (streamScanf (record, format, &val)) return ERROR;
|
||||
if (ao->aslo != 0) val *= ao->aslo;
|
||||
ao->val = val + ao->aoff;
|
||||
return DO_NOT_CONVERT;
|
||||
break;
|
||||
}
|
||||
case DBF_ULONG:
|
||||
case DBF_LONG:
|
||||
{
|
||||
long rval;
|
||||
if (streamScanf (record, format, &rval)) return ERROR;
|
||||
ao->rbv = rval;
|
||||
if (INIT_RUN) ao->rval = rval;
|
||||
ao->rval = rval;
|
||||
if (ao->linr == 0)
|
||||
{
|
||||
/* allow integers with more than 32 bits */
|
||||
if (format->type == DBF_ULONG)
|
||||
val = (unsigned long)rval;
|
||||
else
|
||||
val = rval;
|
||||
break;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
default:
|
||||
return ERROR;
|
||||
}
|
||||
if (ao->aslo != 0.0 && ao->aslo != 1.0) val *= ao->aslo;
|
||||
ao->val = val + ao->aoff;
|
||||
return DO_NOT_CONVERT;
|
||||
}
|
||||
|
||||
static long writeData (dbCommon *record, format_t *format)
|
||||
{
|
||||
aoRecord *ao = (aoRecord *) record;
|
||||
|
||||
double val = (INIT_RUN ? ao->val : ao->oval) - ao->aoff;
|
||||
if (ao->aslo != 0.0 && ao->aslo != 1.0) val /= ao->aslo;
|
||||
|
||||
switch (format->type)
|
||||
{
|
||||
case DBF_DOUBLE:
|
||||
{
|
||||
double val;
|
||||
if (INIT_RUN) val = ao->val;
|
||||
else val = ao->oval;
|
||||
val -= ao->aoff;
|
||||
if (ao->aslo != 0) val /= ao->aslo;
|
||||
return streamPrintf (record, format, val);
|
||||
}
|
||||
case DBF_ULONG:
|
||||
{
|
||||
if (ao->linr == 0)
|
||||
{
|
||||
/* allow integers with more than 32 bits */
|
||||
return streamPrintf (record, format, (unsigned long)val);
|
||||
}
|
||||
return streamPrintf (record, format, (unsigned long)ao->rval);
|
||||
}
|
||||
case DBF_LONG:
|
||||
{
|
||||
if (ao->linr == 0)
|
||||
{
|
||||
/* allow integers with more than 32 bits */
|
||||
return streamPrintf (record, format, (long)val);
|
||||
}
|
||||
return streamPrintf (record, format, (long)ao->rval);
|
||||
}
|
||||
}
|
||||
|
@ -18,9 +18,9 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include "devStream.h"
|
||||
#include <biRecord.h>
|
||||
#include <string.h>
|
||||
#include <biRecord.h>
|
||||
#include "devStream.h"
|
||||
#include <epicsExport.h>
|
||||
|
||||
static long readData (dbCommon *record, format_t *format)
|
||||
@ -30,6 +30,7 @@ static long readData (dbCommon *record, format_t *format)
|
||||
|
||||
switch (format->type)
|
||||
{
|
||||
case DBF_ULONG:
|
||||
case DBF_LONG:
|
||||
{
|
||||
if (streamScanf (record, format, &val)) return ERROR;
|
||||
@ -69,6 +70,7 @@ static long writeData (dbCommon *record, format_t *format)
|
||||
|
||||
switch (format->type)
|
||||
{
|
||||
case DBF_ULONG:
|
||||
case DBF_LONG:
|
||||
{
|
||||
return streamPrintf (record, format, bi->rval);
|
||||
|
@ -18,9 +18,9 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include "devStream.h"
|
||||
#include <boRecord.h>
|
||||
#include <string.h>
|
||||
#include <boRecord.h>
|
||||
#include "devStream.h"
|
||||
#include <epicsExport.h>
|
||||
|
||||
static long readData (dbCommon *record, format_t *format)
|
||||
@ -30,6 +30,7 @@ static long readData (dbCommon *record, format_t *format)
|
||||
|
||||
switch (format->type)
|
||||
{
|
||||
case DBF_ULONG:
|
||||
case DBF_LONG:
|
||||
{
|
||||
if (streamScanf (record, format, &val)) return ERROR;
|
||||
@ -70,6 +71,7 @@ static long writeData (dbCommon *record, format_t *format)
|
||||
|
||||
switch (format->type)
|
||||
{
|
||||
case DBF_ULONG:
|
||||
case DBF_LONG:
|
||||
{
|
||||
return streamPrintf (record, format, bo->rval);
|
||||
|
@ -17,9 +17,9 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include "devStream.h"
|
||||
#include <postfix.h>
|
||||
#include <calcoutRecord.h>
|
||||
#include "devStream.h"
|
||||
#include <epicsExport.h>
|
||||
|
||||
static long readData (dbCommon *record, format_t *format)
|
||||
@ -32,13 +32,17 @@ static long readData (dbCommon *record, format_t *format)
|
||||
{
|
||||
return streamScanf (record, format, &co->val);
|
||||
}
|
||||
case DBF_ULONG:
|
||||
case DBF_LONG:
|
||||
case DBF_ENUM:
|
||||
{
|
||||
long lval;
|
||||
|
||||
if (streamScanf (record, format, &lval)) return ERROR;
|
||||
if (format->type == DBF_LONG)
|
||||
co->val = lval;
|
||||
else
|
||||
co->val = (unsigned long)lval;
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
@ -55,8 +59,12 @@ static long writeData (dbCommon *record, format_t *format)
|
||||
{
|
||||
return streamPrintf (record, format, co->oval);
|
||||
}
|
||||
case DBF_LONG:
|
||||
case DBF_ULONG:
|
||||
case DBF_ENUM:
|
||||
{
|
||||
return streamPrintf (record, format, (unsigned long)co->oval);
|
||||
}
|
||||
case DBF_LONG:
|
||||
{
|
||||
return streamPrintf (record, format, (long)co->oval);
|
||||
}
|
||||
|
@ -18,21 +18,26 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include "devStream.h"
|
||||
#include <longinRecord.h>
|
||||
#include "devStream.h"
|
||||
#include <epicsExport.h>
|
||||
|
||||
static long readData (dbCommon *record, format_t *format)
|
||||
{
|
||||
longinRecord *li = (longinRecord *) record;
|
||||
|
||||
if (format->type == DBF_LONG || format->type == DBF_ENUM)
|
||||
switch (format->type)
|
||||
{
|
||||
case DBF_ULONG:
|
||||
case DBF_LONG:
|
||||
case DBF_ENUM:
|
||||
{
|
||||
long val;
|
||||
if (streamScanf (record, format, &val)) return ERROR;
|
||||
li->val = val;
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
@ -40,8 +45,12 @@ static long writeData (dbCommon *record, format_t *format)
|
||||
{
|
||||
longinRecord *li = (longinRecord *) record;
|
||||
|
||||
if (format->type == DBF_LONG || format->type == DBF_ENUM)
|
||||
switch (format->type)
|
||||
{
|
||||
case DBF_ULONG:
|
||||
case DBF_ENUM:
|
||||
return streamPrintf (record, format, (unsigned long)li->val);
|
||||
case DBF_LONG:
|
||||
return streamPrintf (record, format, (long)li->val);
|
||||
}
|
||||
return ERROR;
|
||||
|
@ -19,21 +19,26 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include "devStream.h"
|
||||
#include <longoutRecord.h>
|
||||
#include "devStream.h"
|
||||
#include <epicsExport.h>
|
||||
|
||||
static long readData (dbCommon *record, format_t *format)
|
||||
{
|
||||
longoutRecord *lo = (longoutRecord *) record;
|
||||
|
||||
if (format->type == DBF_LONG || format->type == DBF_ENUM)
|
||||
switch (format->type)
|
||||
{
|
||||
case DBF_ULONG:
|
||||
case DBF_LONG:
|
||||
case DBF_ENUM:
|
||||
{
|
||||
long val;
|
||||
if (streamScanf (record, format, &val)) return ERROR;
|
||||
lo->val = val;
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
@ -41,8 +46,12 @@ static long writeData (dbCommon *record, format_t *format)
|
||||
{
|
||||
longoutRecord *lo = (longoutRecord *) record;
|
||||
|
||||
if (format->type == DBF_LONG || format->type == DBF_ENUM)
|
||||
switch (format->type)
|
||||
{
|
||||
case DBF_ULONG:
|
||||
case DBF_ENUM:
|
||||
return streamPrintf (record, format, (unsigned long)lo->val);
|
||||
case DBF_LONG:
|
||||
return streamPrintf (record, format, (long)lo->val);
|
||||
}
|
||||
return ERROR;
|
||||
|
@ -19,16 +19,16 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include "devStream.h"
|
||||
#include <mbbiDirectRecord.h>
|
||||
#include "devStream.h"
|
||||
#include <epicsExport.h>
|
||||
|
||||
static long readData (dbCommon *record, format_t *format)
|
||||
{
|
||||
mbbiDirectRecord *mbbiD = (mbbiDirectRecord *) record;
|
||||
long val;
|
||||
unsigned long val;
|
||||
|
||||
if (format->type == DBF_LONG)
|
||||
if (format->type == DBF_ULONG || format->type == DBF_LONG)
|
||||
{
|
||||
if (streamScanf (record, format, &val)) return ERROR;
|
||||
if (mbbiD->mask)
|
||||
@ -40,7 +40,7 @@ static long readData (dbCommon *record, format_t *format)
|
||||
else
|
||||
{
|
||||
/* No MASK, (NOBT = 0): use VAL field */
|
||||
mbbiD->val = (short)val;
|
||||
mbbiD->val = (unsigned short)val;
|
||||
return DO_NOT_CONVERT;
|
||||
}
|
||||
}
|
||||
@ -50,9 +50,9 @@ static long readData (dbCommon *record, format_t *format)
|
||||
static long writeData (dbCommon *record, format_t *format)
|
||||
{
|
||||
mbbiDirectRecord *mbbiD = (mbbiDirectRecord *) record;
|
||||
long val;
|
||||
unsigned long val;
|
||||
|
||||
if (format->type == DBF_LONG)
|
||||
if (format->type == DBF_ULONG || format->type == DBF_LONG)
|
||||
{
|
||||
if (mbbiD->mask) val = mbbiD->rval & mbbiD->mask;
|
||||
else val = mbbiD->val;
|
||||
|
@ -19,19 +19,20 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include "devStream.h"
|
||||
#include <mbbiRecord.h>
|
||||
#include <string.h>
|
||||
#include <mbbiRecord.h>
|
||||
#include "devStream.h"
|
||||
#include <epicsExport.h>
|
||||
|
||||
static long readData (dbCommon *record, format_t *format)
|
||||
{
|
||||
mbbiRecord *mbbi = (mbbiRecord *) record;
|
||||
long val;
|
||||
unsigned long val;
|
||||
int i;
|
||||
|
||||
switch (format->type)
|
||||
{
|
||||
case DBF_ULONG:
|
||||
case DBF_LONG:
|
||||
{
|
||||
if (streamScanf (record, format, &val)) return ERROR;
|
||||
|
@ -19,16 +19,17 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include "devStream.h"
|
||||
#include <mbboDirectRecord.h>
|
||||
#include "alarm.h"
|
||||
#include "devStream.h"
|
||||
#include <epicsExport.h>
|
||||
|
||||
static long readData (dbCommon *record, format_t *format)
|
||||
{
|
||||
mbboDirectRecord *mbboD = (mbboDirectRecord *) record;
|
||||
long val;
|
||||
unsigned long val;
|
||||
|
||||
if (format->type == DBF_LONG)
|
||||
if (format->type == DBF_ULONG || format->type == DBF_LONG)
|
||||
{
|
||||
if (streamScanf (record, format, &val)) return ERROR;
|
||||
if (mbboD->mask)
|
||||
@ -53,7 +54,7 @@ static long writeData (dbCommon *record, format_t *format)
|
||||
mbboDirectRecord *mbboD = (mbboDirectRecord *) record;
|
||||
long val;
|
||||
|
||||
if (format->type == DBF_LONG)
|
||||
if (format->type == DBF_ULONG || format->type == DBF_LONG)
|
||||
{
|
||||
if (mbboD->mask) val = mbboD->rval & mbboD->mask;
|
||||
else val = mbboD->val;
|
||||
@ -67,9 +68,27 @@ static long initRecord (dbCommon *record)
|
||||
mbboDirectRecord *mbboD = (mbboDirectRecord *) record;
|
||||
|
||||
mbboD->mask <<= mbboD->shft;
|
||||
|
||||
/* Workaround for bug in mbboDirect record:
|
||||
Put to VAL overwrites value to 0 if SEVR is INVALID_ALARM
|
||||
Thus first write may send a wrong value.
|
||||
*/
|
||||
mbboD->sevr = 0;
|
||||
return streamInitRecord (record, &mbboD->out, readData, writeData);
|
||||
}
|
||||
|
||||
/* Unfortunately the bug also corrupts the next write to VAL after an I/O error.
|
||||
Thus make sure the record is never left in INVALID_ALARM status.
|
||||
*/
|
||||
|
||||
static long write_mbbo(dbCommon *record)
|
||||
{
|
||||
long status = streamWrite(record);
|
||||
if (record->nsev == INVALID_ALARM) record->nsev = MAJOR_ALARM;
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
struct {
|
||||
long number;
|
||||
DEVSUPFUN report;
|
||||
@ -83,7 +102,7 @@ struct {
|
||||
streamInit,
|
||||
initRecord,
|
||||
streamGetIointInfo,
|
||||
streamWrite
|
||||
write_mbbo
|
||||
};
|
||||
|
||||
epicsExportAddress(dset,devmbboDirectStream);
|
||||
|
@ -19,19 +19,20 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include "devStream.h"
|
||||
#include <mbboRecord.h>
|
||||
#include <string.h>
|
||||
#include <mbboRecord.h>
|
||||
#include "devStream.h"
|
||||
#include <epicsExport.h>
|
||||
|
||||
static long readData (dbCommon *record, format_t *format)
|
||||
{
|
||||
mbboRecord *mbbo = (mbboRecord *) record;
|
||||
long val;
|
||||
unsigned long val;
|
||||
int i;
|
||||
|
||||
switch (format->type)
|
||||
{
|
||||
case DBF_ULONG:
|
||||
case DBF_LONG:
|
||||
{
|
||||
if (streamScanf (record, format, &val)) return ERROR;
|
||||
@ -76,11 +77,12 @@ static long readData (dbCommon *record, format_t *format)
|
||||
static long writeData (dbCommon *record, format_t *format)
|
||||
{
|
||||
mbboRecord *mbbo = (mbboRecord *) record;
|
||||
long val;
|
||||
unsigned long val;
|
||||
int i;
|
||||
|
||||
switch (format->type)
|
||||
{
|
||||
case DBF_ULONG:
|
||||
case DBF_LONG:
|
||||
{
|
||||
/* print VAL or RVAL ? */
|
||||
|