Initial revision
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
TOP=../..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
TEMPLATES_DIR = makeBaseApp
|
||||
|
||||
TEMPLATES += top/Makefile
|
||||
TEMPLATES += top/configure/CONFIG
|
||||
TEMPLATES += top/configure/CONFIG_SITE
|
||||
TEMPLATES += top/configure/Makefile
|
||||
TEMPLATES += top/configure/RELEASE
|
||||
TEMPLATES += top/configure/RULES
|
||||
TEMPLATES += top/configure/RULES.ioc
|
||||
TEMPLATES += top/configure/RULES_DIRS
|
||||
TEMPLATES += top/configure/RULES_TOP
|
||||
|
||||
TEMPLATES += top/supportApp/Makefile
|
||||
TEMPLATES += top/supportApp/Db/Makefile
|
||||
TEMPLATES += top/supportApp/src/Makefile
|
||||
TEMPLATES += top/supportApp/src/_APPNAME_.dbd
|
||||
|
||||
TEMPLATES += top/iocApp/Makefile
|
||||
TEMPLATES += top/iocApp/Db/Makefile
|
||||
TEMPLATES += top/iocApp/src/Makefile
|
||||
TEMPLATES += top/iocApp/src/_APPNAME_Main.cpp
|
||||
|
||||
TEMPLATES += top/exampleApp/Makefile
|
||||
TEMPLATES += top/exampleApp/Db/Makefile
|
||||
TEMPLATES += top/exampleApp/Db/dbExample1.db
|
||||
TEMPLATES += top/exampleApp/Db/dbExample2.db
|
||||
TEMPLATES += top/exampleApp/Db/dbSubExample.db
|
||||
TEMPLATES += top/exampleApp/Db/user.substitutions
|
||||
TEMPLATES += top/exampleApp/Db/userHost.substitutions
|
||||
TEMPLATES += top/exampleApp/src/Makefile
|
||||
TEMPLATES += top/exampleApp/src/xxxRecord.dbd
|
||||
TEMPLATES += top/exampleApp/src/xxxRecord.c
|
||||
TEMPLATES += top/exampleApp/src/devXxxSoft.c
|
||||
TEMPLATES += top/exampleApp/src/xxxSupport.dbd
|
||||
TEMPLATES += top/exampleApp/src/sncExample.stt
|
||||
TEMPLATES += top/exampleApp/src/sncProgram.st
|
||||
TEMPLATES += top/exampleApp/src/sncExample.dbd
|
||||
TEMPLATES += top/exampleApp/src/dbSubExample.c
|
||||
TEMPLATES += top/exampleApp/src/dbSubExample.dbd
|
||||
TEMPLATES += top/exampleApp/src/_APPNAME_Main.cpp
|
||||
TEMPLATES += top/exampleApp/src/_APPNAME_Hello.c
|
||||
TEMPLATES += top/exampleApp/src/_APPNAME_Hello.dbd
|
||||
TEMPLATES += top/exampleApp/src/initTrace.c
|
||||
TEMPLATES += top/exampleApp/src/initTrace.dbd
|
||||
|
||||
TEMPLATES += top/exampleBoot/Makefile
|
||||
TEMPLATES += top/exampleBoot/nfsCommands@vxWorks
|
||||
TEMPLATES += top/exampleBoot/nfsCommands@RTEMS
|
||||
TEMPLATES += top/exampleBoot/ioc/Makefile@Common
|
||||
TEMPLATES += top/exampleBoot/ioc/Makefile@vxWorks
|
||||
TEMPLATES += top/exampleBoot/ioc/Makefile@win32
|
||||
TEMPLATES += top/exampleBoot/ioc/st.cmd@Common
|
||||
TEMPLATES += top/exampleBoot/ioc/st.cmd@vxWorks
|
||||
TEMPLATES += top/exampleBoot/ioc/st.cmd@RTEMS
|
||||
TEMPLATES += top/exampleBoot/ioc/README@Common
|
||||
TEMPLATES += top/exampleBoot/ioc/README@vxWorks
|
||||
|
||||
TEMPLATES += top/caClientApp/Makefile
|
||||
TEMPLATES += top/caClientApp/caExample.c
|
||||
TEMPLATES += top/caClientApp/caMonitor.c
|
||||
|
||||
TEMPLATES += top/caServerApp/Makefile
|
||||
TEMPLATES += top/caServerApp/README
|
||||
TEMPLATES += top/caServerApp/exAsyncPV.cc
|
||||
TEMPLATES += top/caServerApp/exChannel.cc
|
||||
TEMPLATES += top/caServerApp/exPV.cc
|
||||
TEMPLATES += top/caServerApp/exScalarPV.cc
|
||||
TEMPLATES += top/caServerApp/exServer.cc
|
||||
TEMPLATES += top/caServerApp/exServer.h
|
||||
TEMPLATES += top/caServerApp/exVectorPV.cc
|
||||
TEMPLATES += top/caServerApp/main.cc
|
||||
TEMPLATES += top/caServerApp/test.adl
|
||||
TEMPLATES += top/caServerApp/vxEntry.cc
|
||||
|
||||
TEMPLATES += top/iocBoot/Makefile
|
||||
TEMPLATES += top/iocBoot/nfsCommands@vxWorks
|
||||
TEMPLATES += top/iocBoot/nfsCommands@RTEMS
|
||||
TEMPLATES += top/iocBoot/ioc/Makefile@Common
|
||||
TEMPLATES += top/iocBoot/ioc/Makefile@vxWorks
|
||||
TEMPLATES += top/iocBoot/ioc/Makefile@win32
|
||||
TEMPLATES += top/iocBoot/ioc/st.cmd@Common
|
||||
TEMPLATES += top/iocBoot/ioc/st.cmd@Cross
|
||||
TEMPLATES += top/iocBoot/ioc/st.cmd@vxWorks
|
||||
TEMPLATES += top/iocBoot/ioc/st.cmd@RTEMS
|
||||
|
||||
SCRIPTS_HOST += makeBaseApp.pl
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
Executable
+508
@@ -0,0 +1,508 @@
|
||||
eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*-
|
||||
if $running_under_some_shell; # makeBaseApp
|
||||
|
||||
# Authors: Ralph Lange, Marty Kraimer, Andrew Johnson and Janet Anderson
|
||||
# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd
|
||||
|
||||
use Cwd;
|
||||
use Getopt::Std;
|
||||
use File::Find;
|
||||
use File::Path;
|
||||
|
||||
$app_top = cwd();
|
||||
|
||||
%release = (TOP => $app_top);
|
||||
@apps = (TOP);
|
||||
|
||||
$bad_ident_chars = '[^0-9A-Za-z_]';
|
||||
|
||||
&GetUser; # Ensure we know who's in charge
|
||||
&readRelease("configure/RELEASE", \%release, \@apps) if (-r "configure/RELEASE");
|
||||
&readRelease("configure/RELEASE.$ENV{EPICS_HOST_ARCH}", \%release, \@apps)
|
||||
if (-r "configure/RELEASE.$ENV{EPICS_HOST_ARCH}");
|
||||
&expandRelease(\%release, \@apps);
|
||||
&get_commandline_opts; # Check command-line options
|
||||
|
||||
#
|
||||
# Declare two default callback routines for file copy plus two
|
||||
# hook routines to add conversions
|
||||
# These may be overriden within $top/$apptypename/Replace.pl
|
||||
|
||||
# First: the hooks
|
||||
sub ReplaceFilenameHook { return $_[0]; }
|
||||
sub ReplaceLineHook { return $_[0]; }
|
||||
|
||||
# ReplaceFilename
|
||||
# called with the source (template) file or directory name, returns
|
||||
# the target file/dir name (current directory is the application top).
|
||||
|
||||
# Inside iocBoot, templates can install different files for different
|
||||
# IOC architectures or OSs: 'name@<arch>', 'name@<os>' & 'name@Common'
|
||||
# The best match is installed as 'name', but if the best matching file
|
||||
# is empty then no file is created, allowing a file 'name@Common' to
|
||||
# be omitted by providing an empty 'name@<arch>' or 'name@<os>'.
|
||||
|
||||
# Returning an empty string means don't copy this file.
|
||||
sub ReplaceFilename { # (filename)
|
||||
my($file) = $_[0];
|
||||
$file =~ s|.*/CVS/?.*||; # Ignore CVS files and Replace.pl scripts
|
||||
$file =~ s|.*/$apptypename/Replace\.pl$||;
|
||||
|
||||
if($opt_i) {
|
||||
# Handle name@arch stuff, copy only the closest matching file
|
||||
# NB: Won't work with directories, don't use '@' in a directory name!
|
||||
my($base,$filearch) = split /@/, $file;
|
||||
if ($base ne $file) { # This file is arch-specific
|
||||
my($os,$cpu,$toolset) = split /-/, $arch, 3;
|
||||
if (-r "$base\@$arch") { # A version exists for this arch
|
||||
$base = '' unless ($filearch eq $arch && -s $file);
|
||||
} elsif (-r "$base\@$os") { # A version exists for this os
|
||||
$base = '' unless ($filearch eq $os && -s $file);
|
||||
} elsif ( $ENV{EPICS_HOST_ARCH} !~ "$os-$cpu" &&
|
||||
-r "$base\@Cross" ) { # Cross version exists
|
||||
$base = '' unless ($filearch eq "Cross" && -s $file);
|
||||
} elsif (-r "$base\@Common") { # Default version exists
|
||||
$base = '' unless ($filearch eq "Common" && -s $file);
|
||||
} else { # No default version
|
||||
$base = '';
|
||||
}
|
||||
$file = $base; # Strip the @... part from the target name
|
||||
}
|
||||
$file =~ s|/$apptypename|/iocBoot|; # templateBoot => iocBoot
|
||||
}
|
||||
if ($ioc) {
|
||||
$file =~ s|/iocBoot/ioc|/iocBoot/$ioc|; # name the ioc subdirectory
|
||||
$file =~ s|_IOC_|$ioc|;
|
||||
} else {
|
||||
$file =~ s|.*/iocBoot/ioc/?.*||; # Not doing IOCs here
|
||||
}
|
||||
if ($app) {
|
||||
$file =~ s|/$apptypename|/$appdir|; # templateApp => namedApp
|
||||
$file =~ s|/$appdir/configure|/configure/$apptype|;
|
||||
}
|
||||
$file =~ s|_APPNAME_|$appname|;
|
||||
$file =~ s|_APPTYPE_|$apptype|;
|
||||
my $qmtop = quotemeta($top);
|
||||
$file =~ s|$qmtop/||; # Change to the target location
|
||||
$file = &ReplaceFilenameHook($file); # Call the apptype's hook
|
||||
return $file;
|
||||
}
|
||||
|
||||
# ReplaceLine
|
||||
# called with one line of a file, returns the line after replacing
|
||||
# this and that
|
||||
sub ReplaceLine { # (line)
|
||||
my($line) = $_[0];
|
||||
$line =~ s/_IOC_/$ioc/g if ($ioc);
|
||||
$line =~ s/_USER_/$user/go;
|
||||
$line =~ s/_EPICS_BASE_/$app_epics_base/go;
|
||||
$line =~ s/_TEMPLATE_TOP_/$app_template_top/go;
|
||||
$line =~ s/_TOP_/$app_top/go;
|
||||
$line =~ s/_APPNAME_/$appname/g;
|
||||
$line =~ s/_CSAFEAPPNAME_/$csafeappname/g;
|
||||
$line =~ s/_APPTYPE_/$apptype/go;
|
||||
$line =~ s/_ARCH_/$arch/go if ($opt_i);
|
||||
$line = &ReplaceLineHook($line); # Call the apptype's hook
|
||||
return $line;
|
||||
}
|
||||
|
||||
# Source replace overrides for file copy
|
||||
if (-r "$top/$apptypename/Replace.pl") {
|
||||
require "$top/$apptypename/Replace.pl";
|
||||
}
|
||||
|
||||
#
|
||||
# Copy files and dirs from <top> (other than App & Boot) if not present
|
||||
#
|
||||
opendir TOPDIR, "$top" or die "Can't open $top: $!";
|
||||
foreach $f ( grep !/^\.\.?$|^[^\/]*(App|Boot)/, readdir TOPDIR ) {
|
||||
find({wanted => \&FCopyTree, follow => 1}, "$top/$f") unless (-e "$f");
|
||||
}
|
||||
closedir TOPDIR;
|
||||
|
||||
#
|
||||
# Create ioc directories
|
||||
#
|
||||
if ($opt_i) {
|
||||
find({wanted => \&FCopyTree, follow => 1}, "$top/$apptypename");
|
||||
|
||||
$appname=$appnameIn if $appnameIn;
|
||||
foreach $ioc ( @names ) {
|
||||
($appname = $ioc) =~ s/App$// if !$appnameIn;
|
||||
($csafeappname = $appname) =~ s/$bad_ident_chars/_/og;
|
||||
$ioc = "ioc" . $ioc unless ($ioc =~ m/ioc/);
|
||||
if (-d "iocBoot/$ioc") {
|
||||
print "iocBoot/$ioc exists, not modified.\n";
|
||||
next;
|
||||
}
|
||||
find({wanted => \&FCopyTree, follow => 1}, "$top/$apptypename/ioc");
|
||||
}
|
||||
exit 0; # finished here for -i (no xxxApps)
|
||||
}
|
||||
|
||||
#
|
||||
# Create app directories (if any names given)
|
||||
#
|
||||
foreach $app ( @names ) {
|
||||
($appname = $app) =~ s/App$//;
|
||||
($csafeappname = $appname) =~ s/$bad_ident_chars/_/og;
|
||||
$appdir = $appname . "App";
|
||||
if (-d "$appdir") {
|
||||
print "$appname exists, not modified.\n";
|
||||
next;
|
||||
}
|
||||
print "Creating $appname from template type $apptypename\n" if $opt_d;
|
||||
find({wanted => \&FCopyTree, follow => 1}, "$top/$apptypename/");
|
||||
}
|
||||
|
||||
exit 0; # END OF SCRIPT
|
||||
|
||||
#
|
||||
# Get commandline options and check for validity
|
||||
#
|
||||
sub get_commandline_opts { #no args
|
||||
getopts("a:b:dhilp:T:t:") or Cleanup(1);
|
||||
|
||||
# Options help
|
||||
Cleanup(0) if $opt_h;
|
||||
|
||||
# Locate epics_base
|
||||
my ($command) = UnixPath($0);
|
||||
if ($opt_b) { # first choice is -b base
|
||||
$epics_base = UnixPath($opt_b);
|
||||
} elsif ($release{"EPICS_BASE"}) { # second choice is configure/RELEASE
|
||||
$epics_base = UnixPath($release{"EPICS_BASE"});
|
||||
$epics_base =~s|^\$\(TOP\)/||;
|
||||
} elsif ($ENV{EPICS_MBA_BASE}) { # third choice is env var EPICS_MBA_BASE
|
||||
$epics_base = UnixPath($ENV{EPICS_MBA_BASE});
|
||||
} elsif ($command =~ m|/bin/|) { # assume script was run with full path to base
|
||||
$epics_base = $command;
|
||||
$epics_base =~ s|^(.*)/bin/.*makeBaseApp.*|$1|;
|
||||
}
|
||||
$epics_base and -d "$epics_base" or Cleanup(1, "Can't find EPICS base");
|
||||
$app_epics_base = LocalPath($epics_base);
|
||||
$app_epics_base =~ s|^\.\.|\$(TOP)/..|;
|
||||
|
||||
# Locate template top directory
|
||||
if ($opt_T) { # first choice is -T templ-top
|
||||
$top = UnixPath($opt_T);
|
||||
} elsif ($release{"TEMPLATE_TOP"}) { # second choice is configure/RELEASE
|
||||
$top = UnixPath($release{"TEMPLATE_TOP"});
|
||||
$top =~s|^\$\(EPICS_BASE\)|$epics_base|;
|
||||
$top =~s|^\$\(TOP\)/||;
|
||||
}
|
||||
$top = $ENV{EPICS_MBA_TEMPLATE_TOP} unless $top && -d $top; # third choice is env var
|
||||
$top = $epics_base . "/templates/makeBaseApp/top" unless $top && -d $top; # final
|
||||
$top and -d "$top" or Cleanup(1, "Can't find template top directory");
|
||||
$app_template_top = LocalPath($top);
|
||||
$app_template_top =~s|^\.\.|\$(TOP)/..|;
|
||||
$app_template_top =~s|^$epics_base/|\$\(EPICS_BASE\)/|;
|
||||
|
||||
# Print application type list?
|
||||
if ($opt_l) {
|
||||
&ListAppTypes;
|
||||
exit 0; # finished for -l command
|
||||
}
|
||||
|
||||
if (!@ARGV){
|
||||
if ($opt_t) {
|
||||
if ($opt_i) {
|
||||
my @iocs = map {s/iocBoot\///; $_} glob 'iocBoot/ioc*';
|
||||
if (@iocs) {
|
||||
print "The following IOCs already exist here:\n",
|
||||
map {" $_\n"} @iocs;
|
||||
}
|
||||
print "Name the IOC(s) to be created.\n",
|
||||
"Names given will have \"ioc\" prepended to them.\n",
|
||||
"IOC names? ";
|
||||
} else {
|
||||
print "Name the application(s) to be created.\n",
|
||||
"Names given will have \"App\" appended to them.\n",
|
||||
"Application names? ";
|
||||
}
|
||||
$namelist = <STDIN>;
|
||||
chomp($namelist);
|
||||
@names = split /[\s,]/, $namelist;
|
||||
} else {
|
||||
Cleanup(1);
|
||||
}
|
||||
} else {
|
||||
@names = @ARGV;
|
||||
}
|
||||
|
||||
# ioc architecture and application name
|
||||
if ($opt_i && @names) {
|
||||
|
||||
# ioc architecture
|
||||
opendir BINDIR, "$epics_base/bin" or die "Can't open $epics_base/bin: $!";
|
||||
my @archs = grep !/^\./, readdir BINDIR; # exclude .files
|
||||
closedir BINDIR;
|
||||
if ($opt_a) {
|
||||
$arch = $opt_a;
|
||||
} elsif (@archs == 1) {
|
||||
$arch = $archs[0];
|
||||
print "Using target architecture $arch (only one available)\n";
|
||||
} else {
|
||||
print "The following target architectures are available in base:\n";
|
||||
foreach $arch (@archs) {
|
||||
print " $arch\n";
|
||||
}
|
||||
print "What architecture do you want to use? ";
|
||||
$arch = <STDIN>;
|
||||
chomp($arch);
|
||||
}
|
||||
grep /^$arch$/, @archs or Cleanup(1, "Target architecture $arch not available");
|
||||
|
||||
# Application name
|
||||
if ($opt_p){
|
||||
$appnameIn = $opt_p if ($opt_p);
|
||||
} else {
|
||||
my @apps = glob '*App';
|
||||
if (@apps) {
|
||||
print "The following applications are available:\n",
|
||||
map {s/App$//; " $_\n"} @apps;
|
||||
}
|
||||
print "What application should the IOC(s) boot?\n",
|
||||
"The default uses the IOC's name, even if not listed above.\n",
|
||||
"Application name? ";
|
||||
$appnameIn = <STDIN>;
|
||||
chomp($appnameIn);
|
||||
}
|
||||
}
|
||||
|
||||
# Application type
|
||||
$appext = $opt_i ? "Boot" : "App";
|
||||
if ($opt_t) { # first choice is -t type
|
||||
$apptype = $opt_t;
|
||||
$apptype =~ s/$appext$//;
|
||||
} elsif ($ENV{EPICS_MBA_DEF_APP_TYPE}) { # second choice is environment var
|
||||
$apptype = $ENV{EPICS_MBA_DEF_APP_TYPE};
|
||||
$apptype =~ s/(App)|(Boot)$//;
|
||||
} elsif (-d "$top/default$appext") { # third choice is default
|
||||
$apptype = "default";
|
||||
} elsif (-d "$top/example$appext") { # fourth choice is example
|
||||
$apptype = "example";
|
||||
}
|
||||
$apptype or Cleanup(1, "No application type set");
|
||||
$apptypename = $apptype . $appext;
|
||||
(-r "$top/$apptypename") or
|
||||
Cleanup(1, "Can't access template directory '$top/$apptypename'.\n");
|
||||
|
||||
print "\nCommand line / environment options validated:\n"
|
||||
. " Templ-Top: $top\n"
|
||||
. "Templ-Type: $apptype\n"
|
||||
. "Templ-Name: $apptypename\n"
|
||||
. " opt_i: $opt_i\n"
|
||||
. " arch: $arch\n"
|
||||
. "EPICS-Base: $epics_base\n\n" if $opt_d;
|
||||
}
|
||||
|
||||
#
|
||||
# Parse a configure/RELEASE file.
|
||||
#
|
||||
# NB: This subroutine also appears in base/configure/tools/convertRelease.pl
|
||||
# If you make changes here, they will be needed there as well.
|
||||
#
|
||||
sub readRelease {
|
||||
my ($file, $Rmacros, $Rapps) = @_;
|
||||
# $Rmacros is a reference to a hash, $Rapps a ref to an array
|
||||
my ($pre, $var, $post, $macro, $path);
|
||||
local *IN;
|
||||
open(IN, $file) or die "Can't open $file: $!\n";
|
||||
while (<IN>) {
|
||||
chomp;
|
||||
s/\r$//; # Shouldn't need this, but sometimes...
|
||||
s/\s*#.*$//; # Remove trailing comments
|
||||
next if /^\s*$/; # Skip blank lines
|
||||
|
||||
# Expand all already-defined macros in the line:
|
||||
while (($pre,$var,$post) = /(.*)\$\((\w+)\)(.*)/) {
|
||||
last unless (exists $Rmacros->{$var});
|
||||
$_ = $pre . $Rmacros->{$var} . $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 expandRelease {
|
||||
my ($Rmacros, $Rapps) = @_;
|
||||
# $Rmacros is a reference to a hash, $Rapps a ref to an array
|
||||
|
||||
# Expand any (possibly nested) macros that were defined after use
|
||||
while (($macro, $path) = each %$Rmacros) {
|
||||
while (($pre,$var,$post) = $path =~ /(.*)\$\((\w+)\)(.*)/) {
|
||||
$path = $pre . $Rmacros->{$var} . $post;
|
||||
$Rmacros->{$macro} = $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# List application types
|
||||
#
|
||||
sub ListAppTypes { # no args
|
||||
opendir TYPES, "$top" or die "Can't open $top: $!";
|
||||
my @allfiles = readdir TYPES;
|
||||
closedir TYPES;
|
||||
my @apps = grep /.*App$/, @allfiles;
|
||||
my @boots = grep /.*Boot$/, @allfiles;
|
||||
print "Valid application types are:\n";
|
||||
foreach $name (@apps) {
|
||||
$name =~ s|App||;
|
||||
printf "\t$name\n" if ($name && -r "$top/$name" . "App");
|
||||
}
|
||||
print "Valid iocBoot types are:\n";
|
||||
foreach $name (@boots) {
|
||||
$name =~ s|Boot||;
|
||||
printf "\t$name\n" if ($name && -r "$top/$name" . "Boot");;
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Copy a file with replacements
|
||||
#
|
||||
sub CopyFile { # (source)
|
||||
$source = $_[0];
|
||||
$target = &ReplaceFilename($source);
|
||||
|
||||
if ($target and !-e $target) {
|
||||
open(INP, "<$source") and open(OUT, ">$target")
|
||||
or die "$! Copying $source -> $target";
|
||||
|
||||
print "Copying file $source -> $target\n" if $opt_d;
|
||||
while (<INP>) {
|
||||
print OUT &ReplaceLine($_);
|
||||
}
|
||||
close INP; close OUT;
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Find() callback for file or structure copy
|
||||
#
|
||||
sub FCopyTree {
|
||||
chdir $app_top; # Sigh
|
||||
if (-d "$File::Find::name"
|
||||
and ($dir = &ReplaceFilename($File::Find::name))) {
|
||||
print "Creating directory $dir\n" if $opt_d;
|
||||
&mkpath($dir) unless (-d "$dir");
|
||||
} else {
|
||||
&CopyFile($File::Find::name);
|
||||
}
|
||||
chdir $File::Find::dir;
|
||||
}
|
||||
|
||||
#
|
||||
# Cleanup and exit
|
||||
#
|
||||
sub Cleanup { # (return-code [ messsage-line1, line 2, ... ])
|
||||
my ($rtncode, @message) = @_;
|
||||
|
||||
if (@message) {
|
||||
print join("\n", @message), "\n";
|
||||
} else {
|
||||
&Usage;
|
||||
}
|
||||
exit $rtncode;
|
||||
}
|
||||
|
||||
sub Usage {
|
||||
print <<EOF;
|
||||
Usage:
|
||||
<base>/bin/<arch>/makeBaseApp.pl -h
|
||||
display help on command options
|
||||
<base>/bin/<arch>/makeBaseApp.pl -l [options]
|
||||
list application types
|
||||
<base>/bin/<arch>/makeBaseApp.pl -t type [options] [app ...]
|
||||
create application directories
|
||||
<base>/bin/<arch>/makeBaseApp.pl -i -t type [options] [ioc ...]
|
||||
create ioc boot directories
|
||||
where
|
||||
app Application name (the created directory will have \"App\" appended)
|
||||
ioc IOC name (the created directory will have \"ioc\" prepended)
|
||||
EOF
|
||||
print <<EOF if ($opt_h);
|
||||
|
||||
-a arch Set the IOC architecture for use with -i (e.g. vxWorks-68040)
|
||||
If arch is not specified, you will be prompted
|
||||
-b base Set the location of EPICS base (full path)
|
||||
If not specified, base path is taken from configure/RELEASE
|
||||
If configure does not exist, from environment
|
||||
If not found in environment, from makeBaseApp.pl location
|
||||
-d Enable debug messages
|
||||
-i Specifies that ioc boot directories will be generated
|
||||
-l List valid application types for this installation
|
||||
If this is specified the other options are not used
|
||||
-p app Set the application name for use with -i
|
||||
If not specified, you will be prompted
|
||||
-T top Set the template top directory (where the application templates are)
|
||||
If not specified, top path is taken from configure/RELEASE
|
||||
If configure does not exist, top path is taken from environment
|
||||
If not found in environment, the templates from EPICS base are used
|
||||
-t type Set the application type (-l for a list of valid types)
|
||||
If not specified, type is taken from environment
|
||||
If not found in environment, \"default\" is used
|
||||
|
||||
Environment:
|
||||
EPICS_MBA_DEF_APP_TYPE Application type you want to use as default
|
||||
EPICS_MBA_TEMPLATE_TOP Template top directory
|
||||
EPICS_MBA_BASE Location of EPICS base
|
||||
|
||||
Example: Create exampleApp
|
||||
|
||||
<base>/bin/<arch>/makeBaseApp.pl -t example example
|
||||
<base>/bin/<arch>/makeBaseApp.pl -i -t example example
|
||||
EOF
|
||||
}
|
||||
|
||||
sub GetUser {
|
||||
# add to this list if new possibilities arise,
|
||||
# currently it's UNIX and WIN32:
|
||||
$user = $ENV{USER} || $ENV{USERNAME} || Win32::LoginName();
|
||||
$user =~ s/\s+//g; # Bl**dy Windows stupidity...
|
||||
|
||||
unless ($user) {
|
||||
print "Strange, I cannot figure out your user name!\n";
|
||||
print "What should you be called ? ";
|
||||
$user = <STDIN>;
|
||||
chomp $user;
|
||||
}
|
||||
die "No user name" unless $user;
|
||||
}
|
||||
|
||||
# Path rewriting rules for various OSs
|
||||
# These functions are duplicated in configure/convertRelease.pl
|
||||
sub UnixPath {
|
||||
my ($newpath) = @_;
|
||||
if ($^O eq 'cygwin') {
|
||||
$newpath =~ s{\\}{/}go;
|
||||
$newpath =~ s{^([a-zA-Z]):/}{/cygdrive/$1/};
|
||||
} elsif ($^O eq 'MSWin32') {
|
||||
$newpath =~ s{\\}{/}go;
|
||||
} elsif ($^O eq 'sunos') {
|
||||
$newpath =~ s{^/tmp_mnt/}{/};
|
||||
}
|
||||
return $newpath;
|
||||
}
|
||||
|
||||
sub LocalPath {
|
||||
my ($newpath) = @_;
|
||||
if ($^O eq "cygwin") {
|
||||
$newpath =~ s{^/cygdrive/([a-zA-Z])/}{$1:/};
|
||||
} elsif ($^O eq "darwin") {
|
||||
# These rules are likely to be site-specific
|
||||
$newpath =~ s{^/private/var/auto\.home/}{/home/}; # APS
|
||||
}
|
||||
return $newpath;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#Makefile at top of application tree
|
||||
TOP = .
|
||||
include $(TOP)/configure/CONFIG
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), configure)
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *App))
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard iocBoot))
|
||||
|
||||
define DIR_template
|
||||
$(1)_DEPEND_DIRS = configure
|
||||
endef
|
||||
$(foreach dir, $(filter-out configure,$(DIRS)),$(eval $(call DIR_template,$(dir))))
|
||||
|
||||
iocBoot_DEPEND_DIRS += $(filter %App,$(DIRS))
|
||||
|
||||
include $(TOP)/configure/RULES_TOP
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
TOP=..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
#----------------------------------------
|
||||
# ADD MACRO DEFINITIONS AFTER THIS LINE
|
||||
#=============================
|
||||
|
||||
PROD_HOST += caExample
|
||||
caExample_SRCS += caExample.c
|
||||
caExample_LIBS += $(EPICS_BASE_HOST_LIBS)
|
||||
|
||||
PROD_HOST += caMonitor
|
||||
caMonitor_SRCS += caMonitor.c
|
||||
caMonitor_LIBS += $(EPICS_BASE_HOST_LIBS)
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
#----------------------------------------
|
||||
# ADD RULES AFTER THIS LINE
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*caExample.c*/
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cadef.h"
|
||||
|
||||
int main(int argc,char **argv)
|
||||
{
|
||||
double data;
|
||||
chid mychid;
|
||||
|
||||
if(argc != 2) {
|
||||
fprintf(stderr,"usage: caExample pvname\n");
|
||||
exit(1);
|
||||
}
|
||||
SEVCHK(ca_context_create(ca_disable_preemptive_callback),"ca_context_create");
|
||||
SEVCHK(ca_create_channel(argv[1],NULL,NULL,10,&mychid),"ca_create_channel failure");
|
||||
SEVCHK(ca_pend_io(5.0),"ca_pend_io failure");
|
||||
SEVCHK(ca_get(DBR_DOUBLE,mychid,(void *)&data),"ca_get failure");
|
||||
SEVCHK(ca_pend_io(5.0),"ca_pend_io failure");
|
||||
printf("%s %f\n",argv[1],data);
|
||||
return(0);
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*caMonitor.c*/
|
||||
/* This example accepts a file containing a list of pvs to monitor
|
||||
* It prints a message for all ca evemts: connection, access rights, data
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cadef.h"
|
||||
#include "dbDefs.h"
|
||||
#include "epicsString.h"
|
||||
#include "cantProceed.h"
|
||||
|
||||
#define MAX_PV 1000
|
||||
#define MAX_PV_NAME_LEN 40
|
||||
|
||||
typedef struct{
|
||||
char value[20];
|
||||
chid mychid;
|
||||
evid myevid;
|
||||
} MYNODE;
|
||||
|
||||
|
||||
static void printChidInfo(chid chid, char *message)
|
||||
{
|
||||
printf("\n%s\n",message);
|
||||
printf("pv: %s type(%d) nelements(%ld) host(%s)",
|
||||
ca_name(chid),ca_field_type(chid),ca_element_count(chid),
|
||||
ca_host_name(chid));
|
||||
printf(" read(%d) write(%d) state(%d)\n",
|
||||
ca_read_access(chid),ca_write_access(chid),ca_state(chid));
|
||||
}
|
||||
|
||||
static void exceptionCallback(struct exception_handler_args args)
|
||||
{
|
||||
chid chid = args.chid;
|
||||
long stat = args.stat; /* Channel access status code*/
|
||||
const char *channel;
|
||||
static char *noname = "unknown";
|
||||
|
||||
channel = (chid ? ca_name(chid) : noname);
|
||||
|
||||
|
||||
if(chid) printChidInfo(chid,"exceptionCallback");
|
||||
printf("exceptionCallback stat %s channel %s\n",
|
||||
ca_message(stat),channel);
|
||||
}
|
||||
|
||||
static void connectionCallback(struct connection_handler_args args)
|
||||
{
|
||||
chid chid = args.chid;
|
||||
|
||||
printChidInfo(chid,"connectionCallback");
|
||||
}
|
||||
|
||||
static void accessRightsCallback(struct access_rights_handler_args args)
|
||||
{
|
||||
chid chid = args.chid;
|
||||
|
||||
printChidInfo(chid,"accessRightsCallback");
|
||||
}
|
||||
static void eventCallback(struct event_handler_args eha)
|
||||
{
|
||||
chid chid = eha.chid;
|
||||
|
||||
if(eha.status!=ECA_NORMAL) {
|
||||
printChidInfo(chid,"eventCallback");
|
||||
} else {
|
||||
char *pdata = (char *)eha.dbr;
|
||||
printf("Event Callback: %s = %s\n",ca_name(eha.chid),pdata);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc,char **argv)
|
||||
{
|
||||
char *filename;
|
||||
int npv = 0;
|
||||
MYNODE *pmynode[MAX_PV];
|
||||
char *pname[MAX_PV];
|
||||
int i;
|
||||
char tempStr[MAX_PV_NAME_LEN];
|
||||
char *pstr;
|
||||
FILE *fp;
|
||||
|
||||
if (argc != 2) {
|
||||
fprintf(stderr,"usage: caMonitor filename\n");
|
||||
exit(1);
|
||||
}
|
||||
filename = argv[1];
|
||||
fp = fopen(filename,"r");
|
||||
if (!fp) {
|
||||
perror("fopen failed");
|
||||
return(1);
|
||||
}
|
||||
while (npv < MAX_PV) {
|
||||
size_t len;
|
||||
|
||||
pstr = fgets(tempStr, MAX_PV_NAME_LEN, fp);
|
||||
if (!pstr) break;
|
||||
|
||||
len = strlen(pstr);
|
||||
if (len <= 1) continue;
|
||||
|
||||
pstr[len - 1] = '\0'; /* Strip newline */
|
||||
pname[npv] = epicsStrDup(pstr);
|
||||
pmynode[npv] = callocMustSucceed(1, sizeof(MYNODE), "caMonitor");
|
||||
npv++;
|
||||
}
|
||||
SEVCHK(ca_context_create(ca_disable_preemptive_callback),"ca_context_create");
|
||||
SEVCHK(ca_add_exception_event(exceptionCallback,NULL),
|
||||
"ca_add_exception_event");
|
||||
for(i=0; i<npv; i++) {
|
||||
SEVCHK(ca_create_channel(pname[i],connectionCallback,
|
||||
pmynode[i],20,&pmynode[i]->mychid),
|
||||
"ca_create_channel");
|
||||
SEVCHK(ca_replace_access_rights_event(pmynode[i]->mychid,
|
||||
accessRightsCallback),
|
||||
"ca_replace_access_rights_event");
|
||||
SEVCHK(ca_add_event(DBR_STRING,pmynode[i]->mychid,eventCallback,
|
||||
pmynode[i],&pmynode[i]->myevid),
|
||||
"ca_add_event");
|
||||
}
|
||||
/*Should never return from following call*/
|
||||
SEVCHK(ca_pend_event(0.0),"ca_pend_event");
|
||||
return(0);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
# National Laboratory.
|
||||
# Copyright (c) 2002 The Regents of the University of California, as
|
||||
# Operator of Los Alamos National Laboratory.
|
||||
# EPICS BASE Versions 3.13.7
|
||||
# and higher are distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
TOP=..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
PROD_LIBS += $(EPICS_BASE_HOST_LIBS)
|
||||
|
||||
#
|
||||
# Added ws2_32 winmm user32 for the non-dll build
|
||||
#
|
||||
SYS_PROD_LIBS_WIN32 += ws2_32 advapi32 user32
|
||||
|
||||
SRCS += main.cc
|
||||
SRCS += exServer.cc
|
||||
SRCS += exPV.cc
|
||||
SRCS += exVectorPV.cc
|
||||
SRCS += exScalarPV.cc
|
||||
SRCS += exAsyncPV.cc
|
||||
SRCS += exChannel.cc
|
||||
|
||||
PROD_HOST = casexample
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
|
||||
The files in this directory build an example CA server. This code
|
||||
is meant to provide some examples of how the CA server library can
|
||||
be used but is not intended to be an example of exemplary code organization
|
||||
and therefore care should be taken when emulating what is found here.
|
||||
|
||||
The example server exports the process variables (PVs) in the table below.
|
||||
|
||||
ScanPeriod Name HOPR LOPR Type Asynchronous Elements
|
||||
|
||||
.1 "jane" 10.0 0.0 DBR_DOUBLE No 1
|
||||
2.0 "fred" 10.0 -10.0 DBR_DOUBLE No 1
|
||||
.1 "janet" 10.0 0.0 DBR_DOUBLE Yes 1
|
||||
2.0 "freddy"10.0 -10.0 DBR_DOUBLE Yes 1
|
||||
2.0 "alan" 10.0 -10.0 DBR_DOUBLE No 100
|
||||
20.0 "albert"10.0 -10.0 DBR_DOUBLE No 1000
|
||||
-1.0 "boot" 10.0 -10.0 DBR_ENUM No 1
|
||||
-1.0 "booty" 10.0 -10.0 DBR_ENUM Yes 1
|
||||
-1.0 "bill" 10.0 -10.0 DBR_DOUBLE No 1
|
||||
-1.0 "billy" 10.0 -10.0 DBR_DOUBLE Yes 1
|
||||
-1.0 "bloaty"10.0 -10.0 DBR_DOUBLE No 100000
|
||||
|
||||
Many ca servers will find that synchronous variables will meet
|
||||
their needs and will not require the increased complexity
|
||||
associated with asynchronous PVs. Asynchronous PVs are needed
|
||||
when read and write IO requests cant be completed immediately.
|
||||
|
||||
The PVs in the example server are updated periodically if the
|
||||
"ScanPeriod" column above contains a positive number. Some random
|
||||
"noise" is added to a PV's current value each time that it is
|
||||
updated.
|
||||
|
||||
usage:
|
||||
|
||||
excas [-d<debug level> -t<execution time> -p<PV name prefix>
|
||||
-c<numbered alias count> -s<1=scan on (default), 0=scan off>
|
||||
-ss<1=synchronous scan (default), 0=asynchronous scan>]
|
||||
|
||||
-d<debug level>
|
||||
Increased diagnostics messages with increasing debug level. Defaults to no
|
||||
messages.
|
||||
|
||||
-t<execution time>
|
||||
Specifies the duration that the server will run. Defaults to forever.
|
||||
|
||||
-p<PV name prefix>
|
||||
Specifies the prefix applied to all PV names. If you specify "-pxxx:"
|
||||
then clients must specify PV names like "xxx:fred" or "xxx:jane".
|
||||
This is useful when several example servers are running on the same
|
||||
IP subnet for testing purposes. Defaults to no prefix.
|
||||
|
||||
-c<numbered alias count>
|
||||
Useful when you need lots of aliased PV names. The alias names are
|
||||
of the form "fred1", "fred2", etc. Defaults to no aliases.
|
||||
|
||||
-s<1=scan on (default), 0=scan off>
|
||||
Used to turn off updating of the PVs with random noise. Default is
|
||||
to update all PVs with a positive scan period.
|
||||
|
||||
-ss<1=synchronous scan (default), 0=asynchronous scan>
|
||||
Controls updating of PVs from an asynchronous thread (tests thread
|
||||
safety of the server library). Defaults to synchronous.
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
//
|
||||
// Example EPICS CA server
|
||||
// (asynchrronous process variable)
|
||||
//
|
||||
|
||||
#include "exServer.h"
|
||||
|
||||
exAsyncPV::exAsyncPV ( exServer & cas, pvInfo & setup,
|
||||
bool preCreateFlag, bool scanOnIn,
|
||||
double asyncDelayIn ) :
|
||||
exScalarPV ( cas, setup, preCreateFlag, scanOnIn ),
|
||||
asyncDelay ( asyncDelayIn ),
|
||||
simultAsychReadIOCount ( 0u ),
|
||||
simultAsychWriteIOCount ( 0u )
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
// exAsyncPV::read()
|
||||
//
|
||||
caStatus exAsyncPV::read (const casCtx &ctx, gdd &valueIn)
|
||||
{
|
||||
exAsyncReadIO *pIO;
|
||||
|
||||
if ( this->simultAsychReadIOCount >= this->cas.maxSimultAsyncIO () ) {
|
||||
return S_casApp_postponeAsyncIO;
|
||||
}
|
||||
|
||||
pIO = new exAsyncReadIO ( this->cas, ctx,
|
||||
*this, valueIn, this->asyncDelay );
|
||||
if ( ! pIO ) {
|
||||
if ( this->simultAsychReadIOCount > 0 ) {
|
||||
return S_casApp_postponeAsyncIO;
|
||||
}
|
||||
else {
|
||||
return S_casApp_noMemory;
|
||||
}
|
||||
}
|
||||
this->simultAsychReadIOCount++;
|
||||
return S_casApp_asyncCompletion;
|
||||
}
|
||||
|
||||
//
|
||||
// exAsyncPV::writeNotify()
|
||||
//
|
||||
caStatus exAsyncPV::writeNotify ( const casCtx &ctx, const gdd &valueIn )
|
||||
{
|
||||
if ( this->simultAsychWriteIOCount >= this->cas.maxSimultAsyncIO() ) {
|
||||
return S_casApp_postponeAsyncIO;
|
||||
}
|
||||
|
||||
exAsyncWriteIO * pIO = new
|
||||
exAsyncWriteIO ( this->cas, ctx, *this,
|
||||
valueIn, this->asyncDelay );
|
||||
if ( ! pIO ) {
|
||||
if ( this->simultAsychReadIOCount > 0 ) {
|
||||
return S_casApp_postponeAsyncIO;
|
||||
}
|
||||
else {
|
||||
return S_casApp_noMemory;
|
||||
}
|
||||
}
|
||||
this->simultAsychWriteIOCount++;
|
||||
return S_casApp_asyncCompletion;
|
||||
}
|
||||
|
||||
//
|
||||
// exAsyncPV::write()
|
||||
//
|
||||
caStatus exAsyncPV::write ( const casCtx &ctx, const gdd &valueIn )
|
||||
{
|
||||
// implement the discard intermediate values, but last value
|
||||
// sent always applied behavior that IOCs provide excepting
|
||||
// that we will alow N requests to pend instead of a limit
|
||||
// of only one imposed in the IOC
|
||||
if ( this->simultAsychWriteIOCount >= this->cas.maxSimultAsyncIO() ) {
|
||||
pStandbyValue.set ( & valueIn );
|
||||
return S_casApp_success;
|
||||
}
|
||||
|
||||
exAsyncWriteIO * pIO = new
|
||||
exAsyncWriteIO ( this->cas, ctx, *this,
|
||||
valueIn, this->asyncDelay );
|
||||
if ( ! pIO ) {
|
||||
pStandbyValue.set ( & valueIn );
|
||||
return S_casApp_success;
|
||||
}
|
||||
this->simultAsychWriteIOCount++;
|
||||
return S_casApp_asyncCompletion;
|
||||
}
|
||||
|
||||
// Implementing a specialized update for exAsyncPV
|
||||
// allows standby value to update when we update
|
||||
// the PV from an asynchronous write timer expiration
|
||||
// which is a better time compared to removeIO below
|
||||
// which, if used, gets the reads and writes out of
|
||||
// order. This type of reordering can cause the
|
||||
// regression tests to fail.
|
||||
caStatus exAsyncPV :: updateFromAsyncWrite ( const gdd & src )
|
||||
{
|
||||
caStatus stat = this->update ( src );
|
||||
if ( this->simultAsychWriteIOCount <=1 &&
|
||||
pStandbyValue.valid () ) {
|
||||
//printf("updateFromAsyncWrite: write standby\n");
|
||||
stat = this->update ( *this->pStandbyValue );
|
||||
this->pStandbyValue.set ( 0 );
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
void exAsyncPV::removeReadIO ()
|
||||
{
|
||||
if ( this->simultAsychReadIOCount > 0u ) {
|
||||
this->simultAsychReadIOCount--;
|
||||
}
|
||||
else {
|
||||
fprintf ( stderr, "inconsistent simultAsychReadIOCount?\n" );
|
||||
}
|
||||
}
|
||||
|
||||
void exAsyncPV::removeWriteIO ()
|
||||
{
|
||||
if ( this->simultAsychWriteIOCount > 0u ) {
|
||||
this->simultAsychWriteIOCount--;
|
||||
if ( this->simultAsychWriteIOCount == 0 &&
|
||||
pStandbyValue.valid () ) {
|
||||
//printf("removeIO: write standby\n");
|
||||
this->update ( *this->pStandbyValue );
|
||||
this->pStandbyValue.set ( 0 );
|
||||
}
|
||||
}
|
||||
else {
|
||||
fprintf ( stderr, "inconsistent simultAsychWriteIOCount?\n" );
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// exAsyncWriteIO::exAsyncWriteIO()
|
||||
//
|
||||
exAsyncWriteIO::exAsyncWriteIO ( exServer & cas,
|
||||
const casCtx & ctxIn, exAsyncPV & pvIn,
|
||||
const gdd & valueIn, double asyncDelay ) :
|
||||
casAsyncWriteIO ( ctxIn ), pv ( pvIn ),
|
||||
timer ( cas.createTimer () ), pValue(valueIn)
|
||||
{
|
||||
this->timer.start ( *this, asyncDelay );
|
||||
}
|
||||
|
||||
//
|
||||
// exAsyncWriteIO::~exAsyncWriteIO()
|
||||
//
|
||||
exAsyncWriteIO::~exAsyncWriteIO()
|
||||
{
|
||||
this->timer.destroy ();
|
||||
// if the timer hasnt expired, and the value
|
||||
// hasnt been written then force it to happen
|
||||
// now so that regression testing works
|
||||
if ( this->pValue.valid () ) {
|
||||
this->pv.updateFromAsyncWrite ( *this->pValue );
|
||||
}
|
||||
this->pv.removeWriteIO();
|
||||
}
|
||||
|
||||
//
|
||||
// exAsyncWriteIO::expire()
|
||||
// (a virtual function that runs when the base timer expires)
|
||||
//
|
||||
epicsTimerNotify::expireStatus exAsyncWriteIO::
|
||||
expire ( const epicsTime & /* currentTime */ )
|
||||
{
|
||||
assert ( this->pValue.valid () );
|
||||
caStatus status = this->pv.updateFromAsyncWrite ( *this->pValue );
|
||||
this->pValue.set ( 0 );
|
||||
this->postIOCompletion ( status );
|
||||
return noRestart;
|
||||
}
|
||||
|
||||
//
|
||||
// exAsyncReadIO::exAsyncReadIO()
|
||||
//
|
||||
exAsyncReadIO::exAsyncReadIO ( exServer & cas, const casCtx & ctxIn,
|
||||
exAsyncPV & pvIn, gdd & protoIn,
|
||||
double asyncDelay ) :
|
||||
casAsyncReadIO ( ctxIn ), pv ( pvIn ),
|
||||
timer ( cas.createTimer() ), pProto ( protoIn )
|
||||
{
|
||||
this->timer.start ( *this, asyncDelay );
|
||||
}
|
||||
|
||||
//
|
||||
// exAsyncReadIO::~exAsyncReadIO()
|
||||
//
|
||||
exAsyncReadIO::~exAsyncReadIO()
|
||||
{
|
||||
this->pv.removeReadIO ();
|
||||
this->timer.destroy ();
|
||||
}
|
||||
|
||||
//
|
||||
// exAsyncReadIO::expire()
|
||||
// (a virtual function that runs when the base timer expires)
|
||||
//
|
||||
epicsTimerNotify::expireStatus
|
||||
exAsyncReadIO::expire ( const epicsTime & /* currentTime */ )
|
||||
{
|
||||
//
|
||||
// map between the prototype in and the
|
||||
// current value
|
||||
//
|
||||
caStatus status = this->pv.exPV::readNoCtx ( this->pProto );
|
||||
|
||||
//
|
||||
// post IO completion
|
||||
//
|
||||
this->postIOCompletion ( status, *this->pProto );
|
||||
|
||||
return noRestart;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
//
|
||||
// Example EPICS CA server
|
||||
//
|
||||
|
||||
#include "exServer.h"
|
||||
|
||||
//
|
||||
// exChannel::setOwner ()
|
||||
//
|
||||
void exChannel::setOwner(const char * const /* pUserName */,
|
||||
const char * const /* pHostName */)
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
// exChannel::readAccess ()
|
||||
//
|
||||
bool exChannel::readAccess () const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// exChannel::writeAccess ()
|
||||
//
|
||||
bool exChannel::writeAccess () const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,345 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
//
|
||||
// Example EPICS CA server
|
||||
//
|
||||
#include "exServer.h"
|
||||
#include "gddApps.h"
|
||||
|
||||
//
|
||||
// static data for exPV
|
||||
//
|
||||
char exPV::hasBeenInitialized = 0;
|
||||
gddAppFuncTable<exPV> exPV::ft;
|
||||
epicsTime exPV::currentTime;
|
||||
|
||||
//
|
||||
// special gddDestructor guarantees same form of new and delete
|
||||
//
|
||||
class exFixedStringDestructor: public gddDestructor {
|
||||
virtual void run (void *);
|
||||
};
|
||||
|
||||
//
|
||||
// exPV::exPV()
|
||||
//
|
||||
exPV::exPV ( exServer & casIn, pvInfo & setup,
|
||||
bool preCreateFlag, bool scanOnIn ) :
|
||||
cas ( casIn ),
|
||||
timer ( cas.createTimer() ),
|
||||
info ( setup ),
|
||||
interest ( false ),
|
||||
preCreate ( preCreateFlag ),
|
||||
scanOn ( scanOnIn )
|
||||
{
|
||||
//
|
||||
// no dataless PV allowed
|
||||
//
|
||||
assert (this->info.getElementCount()>=1u);
|
||||
|
||||
//
|
||||
// start a very slow background scan
|
||||
// (we will speed this up to the normal rate when
|
||||
// someone is watching the PV)
|
||||
//
|
||||
if ( this->scanOn && this->info.getScanPeriod () > 0.0 ) {
|
||||
this->timer.start ( *this, this->getScanPeriod() );
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// exPV::~exPV()
|
||||
//
|
||||
exPV::~exPV()
|
||||
{
|
||||
this->timer.destroy ();
|
||||
this->info.unlinkPV();
|
||||
}
|
||||
|
||||
//
|
||||
// exPV::destroy()
|
||||
//
|
||||
// this is replaced by a noop since we are
|
||||
// pre-creating most of the PVs during init in this simple server
|
||||
//
|
||||
void exPV::destroy()
|
||||
{
|
||||
if ( ! this->preCreate ) {
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// exPV::update()
|
||||
//
|
||||
caStatus exPV::update ( const gdd & valueIn )
|
||||
{
|
||||
# if DEBUG
|
||||
printf("Setting %s too:\n", this->info.getName().string());
|
||||
valueIn.dump();
|
||||
# endif
|
||||
|
||||
caStatus status = this->updateValue ( valueIn );
|
||||
if ( status || ( ! this->pValue.valid() ) ) {
|
||||
return status;
|
||||
}
|
||||
|
||||
//
|
||||
// post a value change event
|
||||
//
|
||||
caServer * pCAS = this->getCAS();
|
||||
if ( this->interest == true && pCAS != NULL ) {
|
||||
casEventMask select ( pCAS->valueEventMask() | pCAS->logEventMask() );
|
||||
this->postEvent ( select, *this->pValue );
|
||||
}
|
||||
|
||||
return S_casApp_success;
|
||||
}
|
||||
|
||||
//
|
||||
// exScanTimer::expire ()
|
||||
//
|
||||
epicsTimerNotify::expireStatus
|
||||
exPV::expire ( const epicsTime & /*currentTime*/ ) // X aCC 361
|
||||
{
|
||||
this->scan();
|
||||
if ( this->scanOn && this->getScanPeriod() > 0.0 ) {
|
||||
return expireStatus ( restart, this->getScanPeriod() );
|
||||
}
|
||||
else {
|
||||
return noRestart;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// exPV::bestExternalType()
|
||||
//
|
||||
aitEnum exPV::bestExternalType () const
|
||||
{
|
||||
return this->info.getType ();
|
||||
}
|
||||
|
||||
//
|
||||
// exPV::interestRegister()
|
||||
//
|
||||
caStatus exPV::interestRegister ()
|
||||
{
|
||||
if ( ! this->getCAS() ) {
|
||||
return S_casApp_success;
|
||||
}
|
||||
|
||||
this->interest = true;
|
||||
if ( this->scanOn && this->getScanPeriod() > 0.0 &&
|
||||
this->getScanPeriod() < this->timer.getExpireDelay() ) {
|
||||
this->timer.start ( *this, this->getScanPeriod() );
|
||||
}
|
||||
|
||||
return S_casApp_success;
|
||||
}
|
||||
|
||||
//
|
||||
// exPV::interestDelete()
|
||||
//
|
||||
void exPV::interestDelete()
|
||||
{
|
||||
this->interest = false;
|
||||
}
|
||||
|
||||
//
|
||||
// exPV::show()
|
||||
//
|
||||
void exPV::show ( unsigned level ) const
|
||||
{
|
||||
if (level>1u) {
|
||||
if ( this->pValue.valid () ) {
|
||||
printf ( "exPV: cond=%d\n", this->pValue->getStat () );
|
||||
printf ( "exPV: sevr=%d\n", this->pValue->getSevr () );
|
||||
printf ( "exPV: value=%f\n", static_cast < double > ( * this->pValue ) );
|
||||
}
|
||||
printf ( "exPV: interest=%d\n", this->interest );
|
||||
this->timer.show ( level - 1u );
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// exPV::initFT()
|
||||
//
|
||||
void exPV::initFT ()
|
||||
{
|
||||
if ( exPV::hasBeenInitialized ) {
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// time stamp, status, and severity are extracted from the
|
||||
// GDD associated with the "value" application type.
|
||||
//
|
||||
exPV::ft.installReadFunc ("value", &exPV::getValue);
|
||||
exPV::ft.installReadFunc ("precision", &exPV::getPrecision);
|
||||
exPV::ft.installReadFunc ("graphicHigh", &exPV::getHighLimit);
|
||||
exPV::ft.installReadFunc ("graphicLow", &exPV::getLowLimit);
|
||||
exPV::ft.installReadFunc ("controlHigh", &exPV::getHighLimit);
|
||||
exPV::ft.installReadFunc ("controlLow", &exPV::getLowLimit);
|
||||
exPV::ft.installReadFunc ("alarmHigh", &exPV::getHighLimit);
|
||||
exPV::ft.installReadFunc ("alarmLow", &exPV::getLowLimit);
|
||||
exPV::ft.installReadFunc ("alarmHighWarning", &exPV::getHighLimit);
|
||||
exPV::ft.installReadFunc ("alarmLowWarning", &exPV::getLowLimit);
|
||||
exPV::ft.installReadFunc ("units", &exPV::getUnits);
|
||||
exPV::ft.installReadFunc ("enums", &exPV::getEnums);
|
||||
|
||||
exPV::hasBeenInitialized = 1;
|
||||
}
|
||||
|
||||
//
|
||||
// exPV::getPrecision()
|
||||
//
|
||||
caStatus exPV::getPrecision ( gdd & prec )
|
||||
{
|
||||
prec.put(4u);
|
||||
return S_cas_success;
|
||||
}
|
||||
|
||||
//
|
||||
// exPV::getHighLimit()
|
||||
//
|
||||
caStatus exPV::getHighLimit ( gdd & value )
|
||||
{
|
||||
value.put(info.getHopr());
|
||||
return S_cas_success;
|
||||
}
|
||||
|
||||
//
|
||||
// exPV::getLowLimit()
|
||||
//
|
||||
caStatus exPV::getLowLimit ( gdd & value )
|
||||
{
|
||||
value.put(info.getLopr());
|
||||
return S_cas_success;
|
||||
}
|
||||
|
||||
//
|
||||
// exPV::getUnits()
|
||||
//
|
||||
caStatus exPV::getUnits( gdd & units )
|
||||
{
|
||||
aitString str("furlongs", aitStrRefConstImortal);
|
||||
units.put(str);
|
||||
return S_cas_success;
|
||||
}
|
||||
|
||||
//
|
||||
// exPV::getEnums()
|
||||
//
|
||||
// returns the eneumerated state strings
|
||||
// for a discrete channel
|
||||
//
|
||||
// The PVs in this example are purely analog,
|
||||
// and therefore this isnt appropriate in an
|
||||
// analog context ...
|
||||
//
|
||||
caStatus exPV::getEnums ( gdd & enumsIn )
|
||||
{
|
||||
if ( this->info.getType () == aitEnumEnum16 ) {
|
||||
static const unsigned nStr = 2;
|
||||
aitFixedString *str;
|
||||
exFixedStringDestructor *pDes;
|
||||
|
||||
str = new aitFixedString[nStr];
|
||||
if (!str) {
|
||||
return S_casApp_noMemory;
|
||||
}
|
||||
|
||||
pDes = new exFixedStringDestructor;
|
||||
if (!pDes) {
|
||||
delete [] str;
|
||||
return S_casApp_noMemory;
|
||||
}
|
||||
|
||||
strncpy (str[0].fixed_string, "off",
|
||||
sizeof(str[0].fixed_string));
|
||||
strncpy (str[1].fixed_string, "on",
|
||||
sizeof(str[1].fixed_string));
|
||||
|
||||
enumsIn.setDimension(1);
|
||||
enumsIn.setBound (0,0,nStr);
|
||||
enumsIn.putRef (str, pDes);
|
||||
|
||||
return S_cas_success;
|
||||
}
|
||||
|
||||
return S_cas_success;
|
||||
}
|
||||
|
||||
//
|
||||
// exPV::getValue()
|
||||
//
|
||||
caStatus exPV::getValue ( gdd & value )
|
||||
{
|
||||
caStatus status;
|
||||
|
||||
if ( this->pValue.valid () ) {
|
||||
gddStatus gdds;
|
||||
|
||||
gdds = gddApplicationTypeTable::
|
||||
app_table.smartCopy ( &value, & (*this->pValue) );
|
||||
if (gdds) {
|
||||
status = S_cas_noConvert;
|
||||
}
|
||||
else {
|
||||
status = S_cas_success;
|
||||
}
|
||||
}
|
||||
else {
|
||||
status = S_casApp_undefined;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
//
|
||||
// exPV::write()
|
||||
// (synchronous default)
|
||||
//
|
||||
caStatus exPV::write ( const casCtx &, const gdd & valueIn )
|
||||
{
|
||||
return this->update ( valueIn );
|
||||
}
|
||||
|
||||
//
|
||||
// exPV::read()
|
||||
// (synchronous default)
|
||||
//
|
||||
caStatus exPV::read ( const casCtx &, gdd & protoIn )
|
||||
{
|
||||
return this->ft.read ( *this, protoIn );
|
||||
}
|
||||
|
||||
//
|
||||
// exPV::createChannel()
|
||||
//
|
||||
// for access control - optional
|
||||
//
|
||||
casChannel *exPV::createChannel ( const casCtx &ctx,
|
||||
const char * const /* pUserName */,
|
||||
const char * const /* pHostName */ )
|
||||
{
|
||||
return new exChannel ( ctx );
|
||||
}
|
||||
|
||||
//
|
||||
// exFixedStringDestructor::run()
|
||||
//
|
||||
// special gddDestructor guarantees same form of new and delete
|
||||
//
|
||||
void exFixedStringDestructor::run ( void * pUntyped )
|
||||
{
|
||||
aitFixedString *ps = (aitFixedString *) pUntyped;
|
||||
delete [] ps;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "exServer.h"
|
||||
#include "gddApps.h"
|
||||
|
||||
#define myPI 3.14159265358979323846
|
||||
|
||||
//
|
||||
// SUN C++ does not have RAND_MAX yet
|
||||
//
|
||||
#if !defined(RAND_MAX)
|
||||
//
|
||||
// Apparently SUN C++ is using the SYSV version of rand
|
||||
//
|
||||
#if 0
|
||||
#define RAND_MAX INT_MAX
|
||||
#else
|
||||
#define RAND_MAX SHRT_MAX
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//
|
||||
// exScalarPV::scan
|
||||
//
|
||||
void exScalarPV::scan()
|
||||
{
|
||||
caStatus status;
|
||||
double radians;
|
||||
smartGDDPointer pDD;
|
||||
float newValue;
|
||||
float limit;
|
||||
int gddStatus;
|
||||
|
||||
//
|
||||
// update current time (so we are not required to do
|
||||
// this every time that we write the PV which impacts
|
||||
// throughput under sunos4 because gettimeofday() is
|
||||
// slow)
|
||||
//
|
||||
this->currentTime = epicsTime::getCurrent ();
|
||||
|
||||
pDD = new gddScalar ( gddAppType_value, aitEnumFloat64 );
|
||||
if ( ! pDD.valid () ) {
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// smart pointer class manages reference count after this point
|
||||
//
|
||||
gddStatus = pDD->unreference ();
|
||||
assert ( ! gddStatus );
|
||||
|
||||
radians = ( rand () * 2.0 * myPI ) / RAND_MAX;
|
||||
if ( this->pValue.valid () ) {
|
||||
this->pValue->getConvert(newValue);
|
||||
}
|
||||
else {
|
||||
newValue = 0.0f;
|
||||
}
|
||||
newValue += (float) ( sin (radians) / 10.0 );
|
||||
limit = (float) this->info.getHopr ();
|
||||
newValue = tsMin ( newValue, limit );
|
||||
limit = (float) this->info.getLopr ();
|
||||
newValue = tsMax ( newValue, limit );
|
||||
*pDD = newValue;
|
||||
aitTimeStamp gddts ( this->currentTime );
|
||||
pDD->setTimeStamp ( & gddts );
|
||||
status = this->update ( *pDD );
|
||||
if (status!=S_casApp_success) {
|
||||
errMessage ( status, "scalar scan update failed\n" );
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// exScalarPV::updateValue ()
|
||||
//
|
||||
// NOTES:
|
||||
// 1) This should have a test which verifies that the
|
||||
// incoming value in all of its various data types can
|
||||
// be translated into a real number?
|
||||
// 2) We prefer to unreference the old PV value here and
|
||||
// reference the incomming value because this will
|
||||
// result in each value change events retaining an
|
||||
// independent value on the event queue.
|
||||
//
|
||||
caStatus exScalarPV::updateValue ( const gdd & valueIn )
|
||||
{
|
||||
//
|
||||
// Really no need to perform this check since the
|
||||
// server lib verifies that all requests are in range
|
||||
//
|
||||
if ( ! valueIn.isScalar() ) {
|
||||
return S_casApp_outOfBounds;
|
||||
}
|
||||
|
||||
if ( ! pValue.valid () ) {
|
||||
this->pValue = new gddScalar (
|
||||
gddAppType_value, this->info.getType () );
|
||||
if ( ! pValue.valid () ) {
|
||||
return S_casApp_noMemory;
|
||||
}
|
||||
}
|
||||
|
||||
this->pValue->put ( & valueIn );
|
||||
|
||||
return S_casApp_success;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,436 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
//
|
||||
// fileDescriptorManager.process(delay);
|
||||
// (the name of the global symbol has leaked in here)
|
||||
//
|
||||
|
||||
//
|
||||
// Example EPICS CA server
|
||||
//
|
||||
#include "exServer.h"
|
||||
|
||||
//
|
||||
// static list of pre-created PVs
|
||||
//
|
||||
pvInfo exServer::pvList[] = {
|
||||
pvInfo (1.0e-1, "jane", 10.0f, 0.0f, aitEnumFloat64, excasIoSync, 1u),
|
||||
pvInfo (2.0, "fred", 10.0f, -10.0f, aitEnumFloat64, excasIoSync, 1u),
|
||||
pvInfo (1.0e-1, "janet", 10.0f, 0.0f, aitEnumFloat64, excasIoAsync, 1u),
|
||||
pvInfo (2.0, "freddy", 10.0f, -10.0f, aitEnumFloat64, excasIoAsync, 1u),
|
||||
pvInfo (2.0, "alan", 10.0f, -10.0f, aitEnumFloat64, excasIoSync, 100u),
|
||||
pvInfo (20.0, "albert", 10.0f, -10.0f, aitEnumFloat64, excasIoSync, 1000u),
|
||||
pvInfo (-1.0, "boot", 10.0f, -10.0f, aitEnumEnum16, excasIoSync, 1u),
|
||||
pvInfo (1.0, "booty", 10.0f, -10.0f, aitEnumEnum16, excasIoAsync, 1u),
|
||||
pvInfo (-1.0, "bill", 10.0f, -10.0f, aitEnumFloat64, excasIoSync, 1u),
|
||||
pvInfo (-1.0, "billy", 10.0f, -10.0f, aitEnumFloat64, excasIoAsync, 1u)
|
||||
};
|
||||
|
||||
const unsigned exServer::pvListNElem = NELEMENTS (exServer::pvList);
|
||||
|
||||
//
|
||||
// static on-the-fly PVs
|
||||
//
|
||||
pvInfo exServer::billy (-1.0, "billybob", 10.0f, -10.0f, aitEnumFloat64, excasIoAsync, 1u);
|
||||
pvInfo exServer::bloater (.010, "bloater", 10.0f, -10.0f, aitEnumFloat64, excasIoSync, 10000u);
|
||||
pvInfo exServer::bloaty (.010, "bloaty", 10.0f, -10.0f, aitEnumFloat64, excasIoSync, 100000u);
|
||||
|
||||
|
||||
//
|
||||
// exServer::exServer()
|
||||
//
|
||||
exServer::exServer ( const char * const pvPrefix,
|
||||
unsigned aliasCount, bool scanOnIn,
|
||||
bool asyncScan, double asyncDelayIn,
|
||||
unsigned maxSimultAsyncIOIn ) :
|
||||
pTimerQueue ( 0 ), simultAsychIOCount ( 0u ),
|
||||
_maxSimultAsyncIO ( maxSimultAsyncIOIn ),
|
||||
asyncDelay ( asyncDelayIn ), scanOn ( scanOnIn )
|
||||
{
|
||||
unsigned i;
|
||||
exPV *pPV;
|
||||
pvInfo *pPVI;
|
||||
pvInfo *pPVAfter = &exServer::pvList[pvListNElem];
|
||||
char pvAlias[256];
|
||||
const char * const pNameFmtStr = "%.100s%.20s";
|
||||
const char * const pAliasFmtStr = "%.100s%.20s%.6u";
|
||||
|
||||
exPV::initFT();
|
||||
|
||||
if ( asyncScan ) {
|
||||
unsigned timerPriotity;
|
||||
epicsThreadBooleanStatus etbs = epicsThreadLowestPriorityLevelAbove (
|
||||
epicsThreadGetPrioritySelf (), & timerPriotity );
|
||||
if ( etbs != epicsThreadBooleanStatusSuccess ) {
|
||||
timerPriotity = epicsThreadGetPrioritySelf ();
|
||||
}
|
||||
this->pTimerQueue = & epicsTimerQueueActive::allocate ( false, timerPriotity );
|
||||
}
|
||||
|
||||
//
|
||||
// pre-create all of the simple PVs that this server will export
|
||||
//
|
||||
for (pPVI = exServer::pvList; pPVI < pPVAfter; pPVI++) {
|
||||
pPV = pPVI->createPV (*this, true, scanOnIn, this->asyncDelay );
|
||||
if (!pPV) {
|
||||
fprintf(stderr, "Unable to create new PV \"%s\"\n",
|
||||
pPVI->getName());
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Install canonical (root) name
|
||||
//
|
||||
sprintf(pvAlias, pNameFmtStr, pvPrefix, pPVI->getName());
|
||||
this->installAliasName(*pPVI, pvAlias);
|
||||
|
||||
//
|
||||
// Install numbered alias names
|
||||
//
|
||||
for (i=0u; i<aliasCount; i++) {
|
||||
sprintf(pvAlias, pAliasFmtStr, pvPrefix,
|
||||
pPVI->getName(), i);
|
||||
this->installAliasName(*pPVI, pvAlias);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Install create on-the-fly PVs
|
||||
// into the PV name hash table
|
||||
//
|
||||
sprintf ( pvAlias, pNameFmtStr, pvPrefix, billy.getName() );
|
||||
this->installAliasName ( billy, pvAlias );
|
||||
sprintf ( pvAlias, pNameFmtStr, pvPrefix, bloater.getName() );
|
||||
this->installAliasName ( bloater, pvAlias );
|
||||
sprintf ( pvAlias, pNameFmtStr, pvPrefix, bloaty.getName() );
|
||||
this->installAliasName ( bloaty, pvAlias );
|
||||
}
|
||||
|
||||
//
|
||||
// exServer::~exServer()
|
||||
//
|
||||
exServer::~exServer()
|
||||
{
|
||||
this->destroyAllPV ();
|
||||
this->stringResTbl.traverse ( &pvEntry::destroy );
|
||||
}
|
||||
|
||||
void exServer::destroyAllPV ()
|
||||
{
|
||||
for ( unsigned i = 0;
|
||||
i < NELEMENTS(exServer::pvList); i++ ) {
|
||||
exServer::pvList[i].deletePV ();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// exServer::installAliasName()
|
||||
//
|
||||
void exServer::installAliasName(pvInfo &info, const char *pAliasName)
|
||||
{
|
||||
pvEntry *pEntry;
|
||||
|
||||
pEntry = new pvEntry(info, *this, pAliasName);
|
||||
if (pEntry) {
|
||||
int resLibStatus;
|
||||
resLibStatus = this->stringResTbl.add(*pEntry);
|
||||
if (resLibStatus==0) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
delete pEntry;
|
||||
}
|
||||
}
|
||||
fprintf ( stderr,
|
||||
"Unable to enter PV=\"%s\" Alias=\"%s\" in PV name alias hash table\n",
|
||||
info.getName(), pAliasName );
|
||||
}
|
||||
|
||||
//
|
||||
// More advanced pvExistTest() isnt needed so we forward to
|
||||
// original version. This avoids sun pro warnings and speeds
|
||||
// up execution.
|
||||
//
|
||||
pvExistReturn exServer::pvExistTest
|
||||
( const casCtx & ctx, const caNetAddr &, const char * pPVName )
|
||||
{
|
||||
return this->pvExistTest ( ctx, pPVName );
|
||||
}
|
||||
|
||||
//
|
||||
// exServer::pvExistTest()
|
||||
//
|
||||
pvExistReturn exServer::pvExistTest // X aCC 361
|
||||
( const casCtx& ctxIn, const char * pPVName )
|
||||
{
|
||||
//
|
||||
// lifetime of id is shorter than lifetime of pName
|
||||
//
|
||||
stringId id ( pPVName, stringId::refString );
|
||||
pvEntry *pPVE;
|
||||
|
||||
//
|
||||
// Look in hash table for PV name (or PV alias name)
|
||||
//
|
||||
pPVE = this->stringResTbl.lookup ( id );
|
||||
if ( ! pPVE ) {
|
||||
return pverDoesNotExistHere;
|
||||
}
|
||||
|
||||
pvInfo & pvi = pPVE->getInfo();
|
||||
|
||||
//
|
||||
// Initiate async IO if this is an async PV
|
||||
//
|
||||
if ( pvi.getIOType() == excasIoSync ) {
|
||||
return pverExistsHere;
|
||||
}
|
||||
else {
|
||||
if ( this->simultAsychIOCount >= this->_maxSimultAsyncIO ) {
|
||||
return pverDoesNotExistHere;
|
||||
}
|
||||
|
||||
this->simultAsychIOCount++;
|
||||
|
||||
exAsyncExistIO * pIO =
|
||||
new exAsyncExistIO ( pvi, ctxIn, *this );
|
||||
if ( pIO ) {
|
||||
return pverAsyncCompletion;
|
||||
}
|
||||
else {
|
||||
this->simultAsychIOCount--;
|
||||
return pverDoesNotExistHere;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// exServer::pvAttach()
|
||||
//
|
||||
pvAttachReturn exServer::pvAttach // X aCC 361
|
||||
(const casCtx &ctx, const char *pName)
|
||||
{
|
||||
//
|
||||
// lifetime of id is shorter than lifetime of pName
|
||||
//
|
||||
stringId id(pName, stringId::refString);
|
||||
exPV *pPV;
|
||||
pvEntry *pPVE;
|
||||
|
||||
pPVE = this->stringResTbl.lookup(id);
|
||||
if (!pPVE) {
|
||||
return S_casApp_pvNotFound;
|
||||
}
|
||||
|
||||
pvInfo &pvi = pPVE->getInfo();
|
||||
|
||||
//
|
||||
// If this is a synchronous PV create the PV now
|
||||
//
|
||||
if (pvi.getIOType() == excasIoSync) {
|
||||
pPV = pvi.createPV(*this, false, this->scanOn, this->asyncDelay );
|
||||
if (pPV) {
|
||||
return *pPV;
|
||||
}
|
||||
else {
|
||||
return S_casApp_noMemory;
|
||||
}
|
||||
}
|
||||
//
|
||||
// Initiate async IO if this is an async PV
|
||||
//
|
||||
else {
|
||||
if (this->simultAsychIOCount>=this->_maxSimultAsyncIO) {
|
||||
return S_casApp_postponeAsyncIO;
|
||||
}
|
||||
|
||||
this->simultAsychIOCount++;
|
||||
|
||||
exAsyncCreateIO *pIO =
|
||||
new exAsyncCreateIO ( pvi, *this, ctx,
|
||||
this->scanOn, this->asyncDelay );
|
||||
if (pIO) {
|
||||
return S_casApp_asyncCompletion;
|
||||
}
|
||||
else {
|
||||
this->simultAsychIOCount--;
|
||||
return S_casApp_noMemory;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// exServer::setDebugLevel ()
|
||||
//
|
||||
void exServer::setDebugLevel ( unsigned level )
|
||||
{
|
||||
this->caServer::setDebugLevel ( level );
|
||||
}
|
||||
|
||||
//
|
||||
// exServer::createTimer ()
|
||||
//
|
||||
class epicsTimer & exServer::createTimer ()
|
||||
{
|
||||
if ( this->pTimerQueue ) {
|
||||
return this->pTimerQueue->createTimer ();
|
||||
}
|
||||
else {
|
||||
return this->caServer::createTimer ();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// pvInfo::createPV()
|
||||
//
|
||||
exPV *pvInfo::createPV ( exServer & cas, bool preCreateFlag,
|
||||
bool scanOn, double asyncDelay )
|
||||
{
|
||||
if (this->pPV) {
|
||||
return this->pPV;
|
||||
}
|
||||
|
||||
exPV *pNewPV;
|
||||
|
||||
//
|
||||
// create an instance of the appropriate class
|
||||
// depending on the io type and the number
|
||||
// of elements
|
||||
//
|
||||
if (this->elementCount==1u) {
|
||||
switch (this->ioType){
|
||||
case excasIoSync:
|
||||
pNewPV = new exScalarPV ( cas, *this, preCreateFlag, scanOn );
|
||||
break;
|
||||
case excasIoAsync:
|
||||
pNewPV = new exAsyncPV ( cas, *this,
|
||||
preCreateFlag, scanOn, asyncDelay );
|
||||
break;
|
||||
default:
|
||||
pNewPV = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( this->ioType == excasIoSync ) {
|
||||
pNewPV = new exVectorPV ( cas, *this, preCreateFlag, scanOn );
|
||||
}
|
||||
else {
|
||||
pNewPV = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// load initial value (this is not done in
|
||||
// the constructor because the base class's
|
||||
// pure virtual function would be called)
|
||||
//
|
||||
// We always perform this step even if
|
||||
// scanning is disable so that there will
|
||||
// always be an initial value
|
||||
//
|
||||
if (pNewPV) {
|
||||
this->pPV = pNewPV;
|
||||
pNewPV->scan();
|
||||
}
|
||||
|
||||
return pNewPV;
|
||||
}
|
||||
|
||||
//
|
||||
// exServer::show()
|
||||
//
|
||||
void exServer::show (unsigned level) const
|
||||
{
|
||||
//
|
||||
// server tool specific show code goes here
|
||||
//
|
||||
this->stringResTbl.show(level);
|
||||
|
||||
//
|
||||
// print information about ca server libarary
|
||||
// internals
|
||||
//
|
||||
this->caServer::show(level);
|
||||
}
|
||||
|
||||
//
|
||||
// exAsyncExistIO::exAsyncExistIO()
|
||||
//
|
||||
exAsyncExistIO::exAsyncExistIO ( const pvInfo &pviIn, const casCtx &ctxIn,
|
||||
exServer &casIn ) :
|
||||
casAsyncPVExistIO ( ctxIn ), pvi ( pviIn ),
|
||||
timer ( casIn.createTimer () ), cas ( casIn )
|
||||
{
|
||||
this->timer.start ( *this, 0.00001 );
|
||||
}
|
||||
|
||||
//
|
||||
// exAsyncExistIO::~exAsyncExistIO()
|
||||
//
|
||||
exAsyncExistIO::~exAsyncExistIO()
|
||||
{
|
||||
this->cas.removeIO ();
|
||||
this->timer.destroy ();
|
||||
}
|
||||
|
||||
//
|
||||
// exAsyncExistIO::expire()
|
||||
// (a virtual function that runs when the base timer expires)
|
||||
//
|
||||
epicsTimerNotify::expireStatus exAsyncExistIO::expire ( const epicsTime & /*currentTime*/ )
|
||||
{
|
||||
//
|
||||
// post IO completion
|
||||
//
|
||||
this->postIOCompletion ( pvExistReturn(pverExistsHere) );
|
||||
return noRestart;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// exAsyncCreateIO::exAsyncCreateIO()
|
||||
//
|
||||
exAsyncCreateIO ::
|
||||
exAsyncCreateIO ( pvInfo &pviIn, exServer &casIn,
|
||||
const casCtx &ctxIn, bool scanOnIn, double asyncDelayIn ) :
|
||||
casAsyncPVAttachIO ( ctxIn ), pvi ( pviIn ),
|
||||
timer ( casIn.createTimer () ),
|
||||
cas ( casIn ), asyncDelay ( asyncDelayIn ), scanOn ( scanOnIn )
|
||||
{
|
||||
this->timer.start ( *this, 0.00001 );
|
||||
}
|
||||
|
||||
//
|
||||
// exAsyncCreateIO::~exAsyncCreateIO()
|
||||
//
|
||||
exAsyncCreateIO::~exAsyncCreateIO()
|
||||
{
|
||||
this->cas.removeIO ();
|
||||
this->timer.destroy ();
|
||||
}
|
||||
|
||||
//
|
||||
// exAsyncCreateIO::expire()
|
||||
// (a virtual function that runs when the base timer expires)
|
||||
//
|
||||
epicsTimerNotify::expireStatus exAsyncCreateIO::expire ( const epicsTime & /*currentTime*/ )
|
||||
{
|
||||
exPV * pPV = this->pvi.createPV ( this->cas, false,
|
||||
this->scanOn, this->asyncDelay );
|
||||
if ( pPV ) {
|
||||
this->postIOCompletion ( pvAttachReturn ( *pPV ) );
|
||||
}
|
||||
else {
|
||||
this->postIOCompletion ( pvAttachReturn ( S_casApp_noMemory ) );
|
||||
}
|
||||
return noRestart;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,590 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
//
|
||||
// Example EPICS CA server
|
||||
//
|
||||
//
|
||||
// caServer
|
||||
// |
|
||||
// exServer
|
||||
//
|
||||
// casPV
|
||||
// |
|
||||
// exPV-----------
|
||||
// | |
|
||||
// exScalarPV exVectorPV
|
||||
// |
|
||||
// exAsyncPV
|
||||
//
|
||||
// casChannel
|
||||
// |
|
||||
// exChannel
|
||||
//
|
||||
|
||||
|
||||
//
|
||||
// ANSI C
|
||||
//
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
//
|
||||
// EPICS
|
||||
//
|
||||
#define epicsAssertAuthor "Jeff Hill johill@lanl.gov"
|
||||
#include "gddAppFuncTable.h"
|
||||
#include "smartGDDPointer.h"
|
||||
#include "epicsTimer.h"
|
||||
#include "casdef.h"
|
||||
#include "epicsAssert.h"
|
||||
#include "resourceLib.h"
|
||||
#include "tsMinMax.h"
|
||||
|
||||
#ifndef NELEMENTS
|
||||
# define NELEMENTS(A) (sizeof(A)/sizeof(A[0]))
|
||||
#endif
|
||||
|
||||
//
|
||||
// info about all pv in this server
|
||||
//
|
||||
enum excasIoType { excasIoSync, excasIoAsync };
|
||||
|
||||
class exPV;
|
||||
class exServer;
|
||||
|
||||
//
|
||||
// pvInfo
|
||||
//
|
||||
class pvInfo {
|
||||
public:
|
||||
|
||||
pvInfo ( double scanPeriodIn, const char * pNameIn,
|
||||
aitFloat32 hoprIn, aitFloat32 loprIn, aitEnum typeIn,
|
||||
excasIoType ioTypeIn, unsigned countIn );
|
||||
pvInfo ( const pvInfo & copyIn );
|
||||
~pvInfo ();
|
||||
double getScanPeriod () const;
|
||||
const char * getName ()
|
||||
const; double getHopr () const;
|
||||
double getLopr () const;
|
||||
aitEnum getType () const;
|
||||
excasIoType getIOType () const;
|
||||
unsigned getElementCount () const;
|
||||
void unlinkPV ();
|
||||
exPV *createPV ( exServer & exCAS, bool preCreateFlag,
|
||||
bool scanOn, double asyncDelay );
|
||||
void deletePV ();
|
||||
private:
|
||||
const double scanPeriod;
|
||||
const char * pName;
|
||||
const double hopr;
|
||||
const double lopr;
|
||||
aitEnum type;
|
||||
const excasIoType ioType;
|
||||
const unsigned elementCount;
|
||||
exPV * pPV;
|
||||
pvInfo & operator = ( const pvInfo & );
|
||||
};
|
||||
|
||||
//
|
||||
// pvEntry
|
||||
//
|
||||
// o entry in the string hash table for the pvInfo
|
||||
// o Since there may be aliases then we may end up
|
||||
// with several of this class all referencing
|
||||
// the same pv info class (justification
|
||||
// for this breaking out into a seperate class
|
||||
// from pvInfo)
|
||||
//
|
||||
class pvEntry // X aCC 655
|
||||
: public stringId, public tsSLNode < pvEntry > {
|
||||
public:
|
||||
pvEntry ( pvInfo &infoIn, exServer & casIn,
|
||||
const char * pAliasName );
|
||||
~pvEntry();
|
||||
pvInfo & getInfo() const { return this->info; }
|
||||
void destroy ();
|
||||
|
||||
private:
|
||||
pvInfo & info;
|
||||
exServer & cas;
|
||||
pvEntry & operator = ( const pvEntry & );
|
||||
pvEntry ( const pvEntry & );
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// exPV
|
||||
//
|
||||
class exPV : public casPV, public epicsTimerNotify,
|
||||
public tsSLNode < exPV > {
|
||||
public:
|
||||
exPV ( exServer & cas, pvInfo & setup,
|
||||
bool preCreateFlag, bool scanOn );
|
||||
virtual ~exPV();
|
||||
|
||||
void show ( unsigned level ) const;
|
||||
|
||||
//
|
||||
// Called by the server libary each time that it wishes to
|
||||
// subscribe for PV the server tool via postEvent() below.
|
||||
//
|
||||
caStatus interestRegister ();
|
||||
|
||||
//
|
||||
// called by the server library each time that it wishes to
|
||||
// remove its subscription for PV value change events
|
||||
// from the server tool via caServerPostEvents()
|
||||
//
|
||||
void interestDelete ();
|
||||
|
||||
aitEnum bestExternalType () const;
|
||||
|
||||
//
|
||||
// chCreate() is called each time that a PV is attached to
|
||||
// by a client. The server tool must create a casChannel object
|
||||
// (or a derived class) each time that this routine is called
|
||||
//
|
||||
// If the operation must complete asynchronously then return
|
||||
// the status code S_casApp_asyncCompletion and then
|
||||
// create the casChannel object at some time in the future
|
||||
//
|
||||
//casChannel *createChannel ();
|
||||
|
||||
//
|
||||
// This gets called when the pv gets a new value
|
||||
//
|
||||
caStatus update ( const gdd & );
|
||||
|
||||
//
|
||||
// Gets called when we add noise to the current value
|
||||
//
|
||||
virtual void scan () = 0;
|
||||
|
||||
//
|
||||
// If no one is watching scan the PV with 10.0
|
||||
// times the specified period
|
||||
//
|
||||
double getScanPeriod ();
|
||||
|
||||
caStatus read ( const casCtx &, gdd & protoIn );
|
||||
|
||||
caStatus readNoCtx ( smartGDDPointer pProtoIn );
|
||||
|
||||
caStatus write ( const casCtx &, const gdd & value );
|
||||
|
||||
void destroy ();
|
||||
|
||||
const pvInfo & getPVInfo ();
|
||||
|
||||
const char * getName() const;
|
||||
|
||||
static void initFT();
|
||||
|
||||
casChannel * createChannel ( const casCtx &ctx,
|
||||
const char * const pUserName,
|
||||
const char * const pHostName );
|
||||
|
||||
protected:
|
||||
smartGDDPointer pValue;
|
||||
exServer & cas;
|
||||
epicsTimer & timer;
|
||||
pvInfo & info;
|
||||
bool interest;
|
||||
bool preCreate;
|
||||
bool scanOn;
|
||||
static epicsTime currentTime;
|
||||
|
||||
virtual caStatus updateValue ( const gdd & ) = 0;
|
||||
|
||||
private:
|
||||
|
||||
//
|
||||
// scan timer expire
|
||||
//
|
||||
expireStatus expire ( const epicsTime & currentTime );
|
||||
|
||||
//
|
||||
// Std PV Attribute fetch support
|
||||
//
|
||||
gddAppFuncTableStatus getPrecision(gdd &value);
|
||||
gddAppFuncTableStatus getHighLimit(gdd &value);
|
||||
gddAppFuncTableStatus getLowLimit(gdd &value);
|
||||
gddAppFuncTableStatus getUnits(gdd &value);
|
||||
gddAppFuncTableStatus getValue(gdd &value);
|
||||
gddAppFuncTableStatus getEnums(gdd &value);
|
||||
|
||||
exPV & operator = ( const exPV & );
|
||||
exPV ( const exPV & );
|
||||
|
||||
//
|
||||
// static
|
||||
//
|
||||
static gddAppFuncTable<exPV> ft;
|
||||
static char hasBeenInitialized;
|
||||
};
|
||||
|
||||
//
|
||||
// exScalarPV
|
||||
//
|
||||
class exScalarPV : public exPV {
|
||||
public:
|
||||
exScalarPV ( exServer & cas, pvInfo &setup,
|
||||
bool preCreateFlag, bool scanOnIn ) :
|
||||
exPV ( cas, setup,
|
||||
preCreateFlag, scanOnIn) {}
|
||||
void scan();
|
||||
private:
|
||||
caStatus updateValue ( const gdd & );
|
||||
exScalarPV & operator = ( const exScalarPV & );
|
||||
exScalarPV ( const exScalarPV & );
|
||||
};
|
||||
|
||||
//
|
||||
// exVectorPV
|
||||
//
|
||||
class exVectorPV : public exPV {
|
||||
public:
|
||||
exVectorPV ( exServer & cas, pvInfo &setup,
|
||||
bool preCreateFlag, bool scanOnIn ) :
|
||||
exPV ( cas, setup,
|
||||
preCreateFlag, scanOnIn) {}
|
||||
void scan();
|
||||
|
||||
unsigned maxDimension() const;
|
||||
aitIndex maxBound (unsigned dimension) const;
|
||||
|
||||
private:
|
||||
caStatus updateValue ( const gdd & );
|
||||
exVectorPV & operator = ( const exVectorPV & );
|
||||
exVectorPV ( const exVectorPV & );
|
||||
};
|
||||
|
||||
//
|
||||
// exServer
|
||||
//
|
||||
class exServer : private caServer {
|
||||
public:
|
||||
exServer ( const char * const pvPrefix,
|
||||
unsigned aliasCount, bool scanOn,
|
||||
bool asyncScan, double asyncDelay,
|
||||
unsigned maxSimultAsyncIO );
|
||||
~exServer ();
|
||||
void show ( unsigned level ) const;
|
||||
void removeIO ();
|
||||
void removeAliasName ( pvEntry & entry );
|
||||
|
||||
class epicsTimer & createTimer ();
|
||||
void setDebugLevel ( unsigned level );
|
||||
|
||||
void destroyAllPV ();
|
||||
|
||||
unsigned maxSimultAsyncIO () const;
|
||||
|
||||
private:
|
||||
resTable < pvEntry, stringId > stringResTbl;
|
||||
epicsTimerQueueActive * pTimerQueue;
|
||||
unsigned simultAsychIOCount;
|
||||
const unsigned _maxSimultAsyncIO;
|
||||
double asyncDelay;
|
||||
bool scanOn;
|
||||
|
||||
void installAliasName ( pvInfo & info, const char * pAliasName );
|
||||
pvExistReturn pvExistTest ( const casCtx &,
|
||||
const caNetAddr &, const char * pPVName );
|
||||
pvExistReturn pvExistTest ( const casCtx &,
|
||||
const char * pPVName );
|
||||
pvAttachReturn pvAttach ( const casCtx &,
|
||||
const char * pPVName );
|
||||
|
||||
exServer & operator = ( const exServer & );
|
||||
exServer ( const exServer & );
|
||||
|
||||
//
|
||||
// list of pre-created PVs
|
||||
//
|
||||
static pvInfo pvList[];
|
||||
static const unsigned pvListNElem;
|
||||
|
||||
//
|
||||
// on-the-fly PVs
|
||||
//
|
||||
static pvInfo bill;
|
||||
static pvInfo billy;
|
||||
static pvInfo bloater;
|
||||
static pvInfo bloaty;
|
||||
static pvInfo boot;
|
||||
static pvInfo booty;
|
||||
};
|
||||
|
||||
//
|
||||
// exAsyncPV
|
||||
//
|
||||
class exAsyncPV : public exScalarPV {
|
||||
public:
|
||||
exAsyncPV ( exServer & cas, pvInfo &setup,
|
||||
bool preCreateFlag, bool scanOnIn, double asyncDelay );
|
||||
caStatus read ( const casCtx & ctxIn, gdd & protoIn );
|
||||
caStatus write ( const casCtx & ctxIn, const gdd & value );
|
||||
caStatus writeNotify ( const casCtx & ctxIn, const gdd & value );
|
||||
void removeReadIO();
|
||||
void removeWriteIO();
|
||||
caStatus updateFromAsyncWrite ( const gdd & );
|
||||
private:
|
||||
double asyncDelay;
|
||||
smartConstGDDPointer pStandbyValue;
|
||||
unsigned simultAsychReadIOCount;
|
||||
unsigned simultAsychWriteIOCount;
|
||||
exAsyncPV & operator = ( const exAsyncPV & );
|
||||
exAsyncPV ( const exAsyncPV & );
|
||||
};
|
||||
|
||||
//
|
||||
// exChannel
|
||||
//
|
||||
class exChannel : public casChannel{
|
||||
public:
|
||||
exChannel ( const casCtx & ctxIn );
|
||||
void setOwner ( const char * const pUserName,
|
||||
const char * const pHostName );
|
||||
bool readAccess () const;
|
||||
bool writeAccess () const;
|
||||
private:
|
||||
exChannel & operator = ( const exChannel & );
|
||||
exChannel ( const exChannel & );
|
||||
};
|
||||
|
||||
//
|
||||
// exAsyncWriteIO
|
||||
//
|
||||
class exAsyncWriteIO : public casAsyncWriteIO, public epicsTimerNotify {
|
||||
public:
|
||||
exAsyncWriteIO ( exServer &, const casCtx & ctxIn,
|
||||
exAsyncPV &, const gdd &, double asyncDelay );
|
||||
~exAsyncWriteIO ();
|
||||
private:
|
||||
exAsyncPV & pv;
|
||||
epicsTimer & timer;
|
||||
smartConstGDDPointer pValue;
|
||||
expireStatus expire ( const epicsTime & currentTime );
|
||||
exAsyncWriteIO & operator = ( const exAsyncWriteIO & );
|
||||
exAsyncWriteIO ( const exAsyncWriteIO & );
|
||||
};
|
||||
|
||||
//
|
||||
// exAsyncReadIO
|
||||
//
|
||||
class exAsyncReadIO : public casAsyncReadIO, public epicsTimerNotify {
|
||||
public:
|
||||
exAsyncReadIO ( exServer &, const casCtx &,
|
||||
exAsyncPV &, gdd &, double asyncDelay );
|
||||
virtual ~exAsyncReadIO ();
|
||||
private:
|
||||
exAsyncPV & pv;
|
||||
epicsTimer & timer;
|
||||
smartGDDPointer pProto;
|
||||
expireStatus expire ( const epicsTime & currentTime );
|
||||
exAsyncReadIO & operator = ( const exAsyncReadIO & );
|
||||
exAsyncReadIO ( const exAsyncReadIO & );
|
||||
};
|
||||
|
||||
//
|
||||
// exAsyncExistIO
|
||||
// (PV exist async IO)
|
||||
//
|
||||
class exAsyncExistIO : public casAsyncPVExistIO, public epicsTimerNotify {
|
||||
public:
|
||||
exAsyncExistIO ( const pvInfo & pviIn, const casCtx & ctxIn,
|
||||
exServer & casIn );
|
||||
virtual ~exAsyncExistIO ();
|
||||
private:
|
||||
const pvInfo & pvi;
|
||||
epicsTimer & timer;
|
||||
exServer & cas;
|
||||
expireStatus expire ( const epicsTime & currentTime );
|
||||
exAsyncExistIO & operator = ( const exAsyncExistIO & );
|
||||
exAsyncExistIO ( const exAsyncExistIO & );
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// exAsyncCreateIO
|
||||
// (PV create async IO)
|
||||
//
|
||||
class exAsyncCreateIO : public casAsyncPVAttachIO, public epicsTimerNotify {
|
||||
public:
|
||||
exAsyncCreateIO ( pvInfo & pviIn, exServer & casIn,
|
||||
const casCtx & ctxIn, bool scanOnIn, double asyncDelay );
|
||||
virtual ~exAsyncCreateIO ();
|
||||
private:
|
||||
pvInfo & pvi;
|
||||
epicsTimer & timer;
|
||||
exServer & cas;
|
||||
double asyncDelay;
|
||||
bool scanOn;
|
||||
expireStatus expire ( const epicsTime & currentTime );
|
||||
exAsyncCreateIO & operator = ( const exAsyncCreateIO & );
|
||||
exAsyncCreateIO ( const exAsyncCreateIO & );
|
||||
};
|
||||
|
||||
inline pvInfo::pvInfo ( double scanPeriodIn, const char *pNameIn,
|
||||
aitFloat32 hoprIn, aitFloat32 loprIn,
|
||||
aitEnum typeIn, excasIoType ioTypeIn,
|
||||
unsigned countIn ) :
|
||||
|
||||
scanPeriod ( scanPeriodIn ), pName ( pNameIn ),
|
||||
hopr ( hoprIn ), lopr ( loprIn ), type ( typeIn ),
|
||||
ioType ( ioTypeIn ), elementCount ( countIn ),
|
||||
pPV ( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
// for use when MSVC++ will not build a default copy constructor
|
||||
// for this class
|
||||
//
|
||||
inline pvInfo::pvInfo ( const pvInfo & copyIn ) :
|
||||
|
||||
scanPeriod ( copyIn.scanPeriod ), pName ( copyIn.pName ),
|
||||
hopr ( copyIn.hopr ), lopr ( copyIn.lopr ), type ( copyIn.type ),
|
||||
ioType ( copyIn.ioType ), elementCount ( copyIn.elementCount ),
|
||||
pPV ( copyIn.pPV )
|
||||
{
|
||||
}
|
||||
|
||||
inline pvInfo::~pvInfo ()
|
||||
{
|
||||
//
|
||||
// GDD cleanup gets rid of GDD's that are in use
|
||||
// by the PV before the file scope destructer for
|
||||
// this class runs here so this does not seem to
|
||||
// be a good idea
|
||||
//
|
||||
//if ( this->pPV != NULL ) {
|
||||
// delete this->pPV;
|
||||
//}
|
||||
}
|
||||
|
||||
inline void pvInfo::deletePV ()
|
||||
{
|
||||
if ( this->pPV != NULL ) {
|
||||
delete this->pPV;
|
||||
}
|
||||
}
|
||||
|
||||
inline double pvInfo::getScanPeriod () const
|
||||
{
|
||||
return this->scanPeriod;
|
||||
}
|
||||
|
||||
inline const char *pvInfo::getName () const
|
||||
{
|
||||
return this->pName;
|
||||
}
|
||||
|
||||
inline double pvInfo::getHopr () const
|
||||
{
|
||||
return this->hopr;
|
||||
}
|
||||
|
||||
inline double pvInfo::getLopr () const
|
||||
{
|
||||
return this->lopr;
|
||||
}
|
||||
|
||||
inline aitEnum pvInfo::getType () const
|
||||
{
|
||||
return this->type;
|
||||
}
|
||||
|
||||
inline excasIoType pvInfo::getIOType () const
|
||||
{
|
||||
return this->ioType;
|
||||
}
|
||||
|
||||
inline unsigned pvInfo::getElementCount () const
|
||||
{
|
||||
return this->elementCount;
|
||||
}
|
||||
|
||||
inline void pvInfo::unlinkPV ()
|
||||
{
|
||||
this->pPV = NULL;
|
||||
}
|
||||
|
||||
inline pvEntry::pvEntry ( pvInfo & infoIn, exServer & casIn,
|
||||
const char * pAliasName ) :
|
||||
stringId ( pAliasName ), info ( infoIn ), cas ( casIn )
|
||||
{
|
||||
assert ( this->stringId::resourceName() != NULL );
|
||||
}
|
||||
|
||||
inline pvEntry::~pvEntry ()
|
||||
{
|
||||
this->cas.removeAliasName ( *this );
|
||||
}
|
||||
|
||||
inline void pvEntry::destroy ()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
inline void exServer::removeAliasName ( pvEntry & entry )
|
||||
{
|
||||
pvEntry * pE;
|
||||
pE = this->stringResTbl.remove ( entry );
|
||||
assert ( pE == &entry );
|
||||
}
|
||||
|
||||
inline double exPV::getScanPeriod ()
|
||||
{
|
||||
double curPeriod = this->info.getScanPeriod ();
|
||||
if ( ! this->interest ) {
|
||||
curPeriod *= 10.0L;
|
||||
}
|
||||
return curPeriod;
|
||||
}
|
||||
|
||||
inline caStatus exPV::readNoCtx ( smartGDDPointer pProtoIn )
|
||||
{
|
||||
return this->ft.read ( *this, *pProtoIn );
|
||||
}
|
||||
|
||||
inline const pvInfo & exPV::getPVInfo ()
|
||||
{
|
||||
return this->info;
|
||||
}
|
||||
|
||||
inline const char * exPV::getName () const
|
||||
{
|
||||
return this->info.getName();
|
||||
}
|
||||
|
||||
inline void exServer::removeIO()
|
||||
{
|
||||
if ( this->simultAsychIOCount > 0u ) {
|
||||
this->simultAsychIOCount--;
|
||||
}
|
||||
else {
|
||||
fprintf ( stderr,
|
||||
"simultAsychIOCount underflow?\n" );
|
||||
}
|
||||
}
|
||||
|
||||
inline unsigned exServer :: maxSimultAsyncIO () const
|
||||
{
|
||||
return this->_maxSimultAsyncIO;
|
||||
}
|
||||
|
||||
inline exChannel::exChannel ( const casCtx & ctxIn ) :
|
||||
casChannel(ctxIn)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -0,0 +1,266 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#include "exServer.h"
|
||||
#include "gddApps.h"
|
||||
|
||||
#define myPI 3.14159265358979323846
|
||||
|
||||
//
|
||||
// SUN C++ does not have RAND_MAX yet
|
||||
//
|
||||
#if ! defined(RAND_MAX)
|
||||
//
|
||||
// Apparently SUN C++ is using the SYSV version of rand
|
||||
//
|
||||
# if 0
|
||||
# define RAND_MAX INT_MAX
|
||||
# else
|
||||
# define RAND_MAX SHRT_MAX
|
||||
# endif
|
||||
#endif
|
||||
|
||||
//
|
||||
// special gddDestructor guarantees same form of new and delete
|
||||
//
|
||||
class exVecDestructor: public gddDestructor {
|
||||
virtual void run (void *);
|
||||
};
|
||||
|
||||
//
|
||||
// exVectorPV::maxDimension()
|
||||
//
|
||||
unsigned exVectorPV::maxDimension() const
|
||||
{
|
||||
return 1u;
|
||||
}
|
||||
|
||||
//
|
||||
// exVectorPV::maxBound()
|
||||
//
|
||||
aitIndex exVectorPV::maxBound (unsigned dimension) const // X aCC 361
|
||||
{
|
||||
if (dimension==0u) {
|
||||
return this->info.getElementCount();
|
||||
}
|
||||
else {
|
||||
return 0u;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// exVectorPV::scan
|
||||
//
|
||||
void exVectorPV::scan()
|
||||
{
|
||||
caStatus status;
|
||||
double radians;
|
||||
smartGDDPointer pDD;
|
||||
aitFloat32 *pF, *pFE;
|
||||
const aitFloat32 *pCF;
|
||||
float newValue;
|
||||
float limit;
|
||||
exVecDestructor *pDest;
|
||||
int gddStatus;
|
||||
|
||||
//
|
||||
// update current time (so we are not required to do
|
||||
// this every time that we write the PV which impacts
|
||||
// throughput under sunos4 because gettimeofday() is
|
||||
// slow)
|
||||
//
|
||||
this->currentTime = epicsTime::getCurrent();
|
||||
|
||||
pDD = new gddAtomic (gddAppType_value, aitEnumFloat64,
|
||||
1u, this->info.getElementCount());
|
||||
if ( ! pDD.valid () ) {
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// smart pointer class manages reference count after this point
|
||||
//
|
||||
gddStatus = pDD->unreference();
|
||||
assert (!gddStatus);
|
||||
|
||||
//
|
||||
// allocate array buffer
|
||||
//
|
||||
pF = new aitFloat32 [this->info.getElementCount()];
|
||||
if (!pF) {
|
||||
return;
|
||||
}
|
||||
|
||||
pDest = new exVecDestructor;
|
||||
if (!pDest) {
|
||||
delete [] pF;
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// install the buffer into the DD
|
||||
// (do this before we increment pF)
|
||||
//
|
||||
pDD->putRef(pF, pDest);
|
||||
|
||||
//
|
||||
// double check for reasonable bounds on the
|
||||
// current value
|
||||
//
|
||||
pCF=NULL;
|
||||
if ( this->pValue.valid () ) {
|
||||
if (this->pValue->dimension()==1u) {
|
||||
const gddBounds *pB = this->pValue->getBounds();
|
||||
if (pB[0u].size()==this->info.getElementCount()) {
|
||||
pCF = *this->pValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pFE = &pF[this->info.getElementCount()];
|
||||
while (pF<pFE) {
|
||||
radians = (rand () * 2.0 * myPI)/RAND_MAX;
|
||||
if (pCF) {
|
||||
newValue = *pCF++;
|
||||
}
|
||||
else {
|
||||
newValue = 0.0f;
|
||||
}
|
||||
newValue += (float) (sin (radians) / 10.0);
|
||||
limit = (float) this->info.getHopr();
|
||||
newValue = tsMin (newValue, limit);
|
||||
limit = (float) this->info.getLopr();
|
||||
newValue = tsMax (newValue, limit);
|
||||
*(pF++) = newValue;
|
||||
}
|
||||
|
||||
aitTimeStamp gddts = this->currentTime;
|
||||
pDD->setTimeStamp ( & gddts );
|
||||
|
||||
status = this->update ( *pDD );
|
||||
if ( status != S_casApp_success ) {
|
||||
errMessage (status, "vector scan update failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// exVectorPV::updateValue ()
|
||||
//
|
||||
// NOTES:
|
||||
// 1) This should have a test which verifies that the
|
||||
// incoming value in all of its various data types can
|
||||
// be translated into a real number?
|
||||
// 2) We prefer to unreference the old PV value here and
|
||||
// reference the incomming value because this will
|
||||
// result in value change events each retaining an
|
||||
// independent value on the event queue. With large arrays
|
||||
// this may result in too much memory consumtion on
|
||||
// the event queue.
|
||||
//
|
||||
caStatus exVectorPV::updateValue ( const gdd & value )
|
||||
{
|
||||
|
||||
//
|
||||
// Check bounds of incoming request
|
||||
// (and see if we are replacing all elements -
|
||||
// replaceOk==true)
|
||||
//
|
||||
// Perhaps much of this is unnecessary since the
|
||||
// server lib checks the bounds of all requests
|
||||
//
|
||||
if ( value.isAtomic()) {
|
||||
if ( value.dimension() != 1u ) {
|
||||
return S_casApp_badDimension;
|
||||
}
|
||||
const gddBounds* pb = value.getBounds ();
|
||||
if ( pb[0u].first() != 0u ) {
|
||||
return S_casApp_outOfBounds;
|
||||
}
|
||||
else if ( pb[0u].size() > this->info.getElementCount() ) {
|
||||
return S_casApp_outOfBounds;
|
||||
}
|
||||
}
|
||||
else if ( ! value.isScalar() ) {
|
||||
//
|
||||
// no containers
|
||||
//
|
||||
return S_casApp_outOfBounds;
|
||||
}
|
||||
|
||||
//
|
||||
// Create a new array data descriptor
|
||||
// (so that old values that may be referenced on the
|
||||
// event queue are not replaced)
|
||||
//
|
||||
smartGDDPointer pNewValue ( new gddAtomic ( gddAppType_value, aitEnumFloat64,
|
||||
1u, this->info.getElementCount() ) );
|
||||
if ( ! pNewValue.valid() ) {
|
||||
return S_casApp_noMemory;
|
||||
}
|
||||
|
||||
//
|
||||
// smart pointer class takes care of the reference count
|
||||
// from here down
|
||||
//
|
||||
gddStatus gdds = pNewValue->unreference( );
|
||||
assert ( ! gdds );
|
||||
|
||||
//
|
||||
// allocate array buffer
|
||||
//
|
||||
aitFloat64 * pF = new aitFloat64 [this->info.getElementCount()];
|
||||
if (!pF) {
|
||||
return S_casApp_noMemory;
|
||||
}
|
||||
|
||||
//
|
||||
// Install (and initialize) array buffer
|
||||
// if no old values exist
|
||||
//
|
||||
unsigned count = this->info.getElementCount();
|
||||
for ( unsigned i = 0u; i < count; i++ ) {
|
||||
pF[i] = 0.0f;
|
||||
}
|
||||
|
||||
exVecDestructor * pDest = new exVecDestructor;
|
||||
if (!pDest) {
|
||||
delete [] pF;
|
||||
return S_casApp_noMemory;
|
||||
}
|
||||
|
||||
//
|
||||
// install the buffer into the DD
|
||||
// (do this before we increment pF)
|
||||
//
|
||||
pNewValue->putRef ( pF, pDest );
|
||||
|
||||
//
|
||||
// copy in the values that they are writing
|
||||
//
|
||||
gdds = pNewValue->put( & value );
|
||||
if ( gdds ) {
|
||||
return S_cas_noConvert;
|
||||
}
|
||||
|
||||
this->pValue = pNewValue;
|
||||
|
||||
return S_casApp_success;
|
||||
}
|
||||
|
||||
//
|
||||
// exVecDestructor::run()
|
||||
//
|
||||
// special gddDestructor guarantees same form of new and delete
|
||||
//
|
||||
void exVecDestructor::run ( void *pUntyped )
|
||||
{
|
||||
aitFloat32 * pf = reinterpret_cast < aitFloat32 * > ( pUntyped );
|
||||
delete [] pf;
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#include "envDefs.h"
|
||||
#include "errlog.h"
|
||||
|
||||
#include "exServer.h"
|
||||
#include "fdManager.h"
|
||||
|
||||
//
|
||||
// main()
|
||||
// (example single threaded ca server tool main loop)
|
||||
//
|
||||
extern int main ( int argc, const char **argv )
|
||||
{
|
||||
epicsTime begin (epicsTime::getCurrent());
|
||||
exServer *pCAS;
|
||||
unsigned debugLevel = 0u;
|
||||
double executionTime = 0.0;
|
||||
double asyncDelay = 0.1;
|
||||
char pvPrefix[128] = "";
|
||||
unsigned aliasCount = 1u;
|
||||
unsigned scanOn = true;
|
||||
unsigned syncScan = true;
|
||||
char arraySize[64] = "";
|
||||
bool forever = true;
|
||||
unsigned maxSimultAsyncIO = 1000u;
|
||||
int i;
|
||||
|
||||
i = 1;
|
||||
while ( i < argc ) {
|
||||
if ( strcmp ( argv[i], "-d" ) == 0 ) {
|
||||
if ( i+1 < argc && sscanf ( argv[i+1], "%u", & debugLevel ) == 1 ) {
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ( strcmp ( argv[i],"-t" ) == 0 ) {
|
||||
if ( i+1 < argc && sscanf ( argv[i+1], "%lf", & executionTime ) == 1 ) {
|
||||
forever = false;
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ( strcmp ( argv[i], "-p" ) == 0 ) {
|
||||
if ( i+1 < argc && sscanf ( argv[i+1], "%127s", pvPrefix ) == 1 ) {
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ( strcmp ( argv[i], "-c" ) == 0 ) {
|
||||
if ( i+1 < argc && sscanf ( argv[i+1], "%u", & aliasCount ) == 1 ) {
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ( strcmp ( argv[i], "-s" ) == 0 ) {
|
||||
if ( i+1 < argc && sscanf ( argv[i+1], "%u", & scanOn ) == 1 ) {
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ( strcmp ( argv[i], "-a" ) == 0 ) {
|
||||
if ( i+1 < argc && sscanf ( argv[i+1], "%63s", arraySize ) == 1 ) {
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ( strcmp ( argv[i],"-ss" ) == 0 ) {
|
||||
if ( i+1 < argc && sscanf ( argv[i+1], "%u", & syncScan ) == 1 ) {
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ( strcmp ( argv[i],"-ad" ) == 0 ) {
|
||||
if ( i+1 < argc && sscanf ( argv[i+1], "%lf", & asyncDelay ) == 1 ) {
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ( strcmp ( argv[i],"-an" ) == 0 ) {
|
||||
if ( i+1 < argc && sscanf ( argv[i+1], "%u", & maxSimultAsyncIO ) == 1 ) {
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
printf ( "\"%s\"?\n", argv[i] );
|
||||
if ( i + 1 < argc ) {
|
||||
printf ( "\"%s\"?\n", argv[i+1] );
|
||||
}
|
||||
printf (
|
||||
"usage: %s [-d <debug level> -t <execution time> -p <PV name prefix> "
|
||||
"-c <numbered alias count> -s <1=scan on (default), 0=scan off> "
|
||||
"-ss <1=synchronous scan (default), 0=asynchronous scan> "
|
||||
"-a <max array size> -ad <async delay> "
|
||||
"-an <max simultaneous async>\n",
|
||||
argv[0]);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
if ( arraySize[0] != '\0' ) {
|
||||
epicsEnvSet ( "EPICS_CA_MAX_ARRAY_BYTES", arraySize );
|
||||
}
|
||||
|
||||
try {
|
||||
pCAS = new exServer ( pvPrefix, aliasCount,
|
||||
scanOn != 0, syncScan == 0, asyncDelay,
|
||||
maxSimultAsyncIO );
|
||||
}
|
||||
catch ( ... ) {
|
||||
errlogPrintf ( "Server initialization error\n" );
|
||||
errlogFlush ();
|
||||
return (-1);
|
||||
}
|
||||
|
||||
pCAS->setDebugLevel(debugLevel);
|
||||
|
||||
if ( forever ) {
|
||||
//
|
||||
// loop here forever
|
||||
//
|
||||
while (true) {
|
||||
fileDescriptorManager.process(1000.0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
double delay = epicsTime::getCurrent() - begin;
|
||||
|
||||
//
|
||||
// loop here untill the specified execution time
|
||||
// expires
|
||||
//
|
||||
while ( delay < executionTime ) {
|
||||
fileDescriptorManager.process ( executionTime - delay );
|
||||
delay = epicsTime::getCurrent() - begin;
|
||||
}
|
||||
}
|
||||
//pCAS->show(2u);
|
||||
delete pCAS;
|
||||
errlogFlush ();
|
||||
return (0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,844 @@
|
||||
file {
|
||||
name="test.dl"
|
||||
}
|
||||
display {
|
||||
magic="305419896"
|
||||
majv="2"
|
||||
mnrv="4"
|
||||
ndyng="0"
|
||||
npc="5"
|
||||
nstr="8"
|
||||
ndynamic="12"
|
||||
nplot="0"
|
||||
nrd="0"
|
||||
nes="0"
|
||||
nkd="0"
|
||||
object {
|
||||
x="0"
|
||||
y="0"
|
||||
width="421"
|
||||
height="306"
|
||||
}
|
||||
clr="0"
|
||||
bclr="1"
|
||||
nwords_dspy="1106"
|
||||
nwords_sta="28"
|
||||
nwords_cmap="36"
|
||||
nwords_crules="106"
|
||||
odyng="306"
|
||||
osta="278"
|
||||
odynamic="306"
|
||||
oplot="1106"
|
||||
ord="1106"
|
||||
oes="1106"
|
||||
okd="1106"
|
||||
opc="58"
|
||||
ostr="88"
|
||||
ocmap="136"
|
||||
ocrules="172"
|
||||
style="solid"
|
||||
fill="outline"
|
||||
width="0"
|
||||
clrmod="static"
|
||||
vismod="static"
|
||||
clrrule="alarm"
|
||||
pv=""
|
||||
cmap=""
|
||||
}
|
||||
"<<color map>>" {
|
||||
ncolors="8"
|
||||
dl_color {
|
||||
r="255"
|
||||
g="255"
|
||||
b="255"
|
||||
inten="255"
|
||||
blink="off"
|
||||
RISCpad="128"
|
||||
}
|
||||
dl_color {
|
||||
r="0"
|
||||
g="0"
|
||||
b="0"
|
||||
inten="0"
|
||||
blink="off"
|
||||
RISCpad="75"
|
||||
}
|
||||
dl_color {
|
||||
r="255"
|
||||
g="0"
|
||||
b="0"
|
||||
inten="255"
|
||||
blink="off"
|
||||
RISCpad="-14684"
|
||||
}
|
||||
dl_color {
|
||||
r="255"
|
||||
g="0"
|
||||
b="0"
|
||||
inten="255"
|
||||
blink="on"
|
||||
RISCpad="14744"
|
||||
}
|
||||
dl_color {
|
||||
r="255"
|
||||
g="255"
|
||||
b="0"
|
||||
inten="255"
|
||||
blink="off"
|
||||
RISCpad="-16536"
|
||||
}
|
||||
dl_color {
|
||||
r="255"
|
||||
g="255"
|
||||
b="0"
|
||||
inten="255"
|
||||
blink="on"
|
||||
RISCpad="-15536"
|
||||
}
|
||||
dl_color {
|
||||
r="0"
|
||||
g="0"
|
||||
b="255"
|
||||
inten="255"
|
||||
blink="off"
|
||||
RISCpad="-28408"
|
||||
}
|
||||
dl_color {
|
||||
r="0"
|
||||
g="0"
|
||||
b="255"
|
||||
inten="255"
|
||||
blink="on"
|
||||
RISCpad="0"
|
||||
}
|
||||
}
|
||||
"<<color rules>>" {
|
||||
nrules="1"
|
||||
dl_color_rule {
|
||||
name="alarm"
|
||||
info[0] {
|
||||
chan="$(C).SEVR"
|
||||
value="MAJOR"
|
||||
connector="use"
|
||||
comparator="equals"
|
||||
clr="2"
|
||||
RISCpad="0"
|
||||
}
|
||||
info[1] {
|
||||
chan="$(C).SEVR"
|
||||
value="MINOR"
|
||||
connector="use"
|
||||
comparator="equals"
|
||||
clr="4"
|
||||
RISCpad="127"
|
||||
}
|
||||
info[2] {
|
||||
chan="$(C).SEVR"
|
||||
value="INFO"
|
||||
connector="use"
|
||||
comparator="equals"
|
||||
clr="6"
|
||||
RISCpad="44"
|
||||
}
|
||||
info[3] {
|
||||
chan=""
|
||||
value=""
|
||||
connector="use"
|
||||
comparator="equals"
|
||||
clr="1"
|
||||
RISCpad="-128"
|
||||
}
|
||||
info[4] {
|
||||
chan=""
|
||||
value=""
|
||||
connector="use"
|
||||
comparator="equals"
|
||||
clr="1"
|
||||
RISCpad="-1"
|
||||
}
|
||||
info[5] {
|
||||
chan=""
|
||||
value=""
|
||||
connector="use"
|
||||
comparator="equals"
|
||||
clr="1"
|
||||
RISCpad="-104"
|
||||
}
|
||||
info[6] {
|
||||
chan=""
|
||||
value=""
|
||||
connector="use"
|
||||
comparator="equals"
|
||||
clr="1"
|
||||
RISCpad="-1"
|
||||
}
|
||||
info[7] {
|
||||
chan=""
|
||||
value=""
|
||||
connector="use"
|
||||
comparator="equals"
|
||||
clr="1"
|
||||
RISCpad="8"
|
||||
}
|
||||
info[8] {
|
||||
chan=""
|
||||
value=""
|
||||
connector="use"
|
||||
comparator="equals"
|
||||
clr="1"
|
||||
RISCpad="120"
|
||||
}
|
||||
info[9] {
|
||||
chan=""
|
||||
value=""
|
||||
connector="use"
|
||||
comparator="equals"
|
||||
clr="1"
|
||||
RISCpad="1"
|
||||
}
|
||||
info[10] {
|
||||
chan=""
|
||||
value=""
|
||||
connector="use"
|
||||
comparator="equals"
|
||||
clr="1"
|
||||
RISCpad="7"
|
||||
}
|
||||
info[11] {
|
||||
chan=""
|
||||
value=""
|
||||
connector="use"
|
||||
comparator="equals"
|
||||
clr="1"
|
||||
RISCpad="19"
|
||||
}
|
||||
info[12] {
|
||||
chan=""
|
||||
value=""
|
||||
connector="use"
|
||||
comparator="equals"
|
||||
clr="1"
|
||||
RISCpad="48"
|
||||
}
|
||||
info[13] {
|
||||
chan=""
|
||||
value=""
|
||||
connector="use"
|
||||
comparator="equals"
|
||||
clr="1"
|
||||
RISCpad="28"
|
||||
}
|
||||
info[14] {
|
||||
chan=""
|
||||
value=""
|
||||
connector="use"
|
||||
comparator="equals"
|
||||
clr="1"
|
||||
RISCpad="-88"
|
||||
}
|
||||
info[15] {
|
||||
chan=""
|
||||
value=""
|
||||
connector="use"
|
||||
comparator="equals"
|
||||
clr="1"
|
||||
RISCpad="0"
|
||||
}
|
||||
fg_enable="on"
|
||||
bg_enable="on"
|
||||
default_fg="0"
|
||||
default_bg="1"
|
||||
}
|
||||
}
|
||||
"<<basic attribute>>" {
|
||||
attr {
|
||||
clr="0"
|
||||
style="solid"
|
||||
fill="outline"
|
||||
width="0"
|
||||
}
|
||||
}
|
||||
"text" {
|
||||
object {
|
||||
x="44"
|
||||
y="16"
|
||||
width="104"
|
||||
height="14"
|
||||
groupid="0"
|
||||
}
|
||||
textix="Sync"
|
||||
align="horiz. left"
|
||||
RISC_pad="0"
|
||||
}
|
||||
"text" {
|
||||
object {
|
||||
x="260"
|
||||
y="13"
|
||||
width="92"
|
||||
height="17"
|
||||
groupid="0"
|
||||
}
|
||||
textix="Async"
|
||||
align="horiz. left"
|
||||
RISC_pad="0"
|
||||
}
|
||||
"indicator" {
|
||||
object {
|
||||
x="15"
|
||||
y="88"
|
||||
width="170"
|
||||
height="22"
|
||||
groupid="0"
|
||||
}
|
||||
monitor {
|
||||
chan="fred"
|
||||
clr="0"
|
||||
bclr="1"
|
||||
label="limits"
|
||||
clrmod="static"
|
||||
rulechan[0] = ""
|
||||
rulechan[1] = ""
|
||||
rulechan[2] = ""
|
||||
rulechan[3] = ""
|
||||
rulechan[4] = ""
|
||||
rulechan[5] = ""
|
||||
rulechan[6] = ""
|
||||
rulechan[7] = ""
|
||||
rulechan[8] = ""
|
||||
rulechan[9] = ""
|
||||
rulechan[10] = ""
|
||||
rulechan[11] = ""
|
||||
rulechan[12] = ""
|
||||
rulechan[13] = ""
|
||||
rulechan[14] = ""
|
||||
rulechan[15] = ""
|
||||
clrrule="alarm"
|
||||
clrargs=""
|
||||
rulecolorbg="0"
|
||||
rulecolorfg="0"
|
||||
hdl="0"
|
||||
ldl="0"
|
||||
prec="-1"
|
||||
newunits=""
|
||||
units="none"
|
||||
decorate="none"
|
||||
convertFunc=""
|
||||
convertParams=""
|
||||
}
|
||||
direction="down"
|
||||
RISC_pad="0"
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x="16"
|
||||
y="133"
|
||||
width="169"
|
||||
height="17"
|
||||
groupid="0"
|
||||
}
|
||||
monitor {
|
||||
chan="fred"
|
||||
clr="0"
|
||||
bclr="1"
|
||||
label="none"
|
||||
clrmod="static"
|
||||
rulechan[0] = ""
|
||||
rulechan[1] = ""
|
||||
rulechan[2] = ""
|
||||
rulechan[3] = ""
|
||||
rulechan[4] = ""
|
||||
rulechan[5] = ""
|
||||
rulechan[6] = ""
|
||||
rulechan[7] = ""
|
||||
rulechan[8] = ""
|
||||
rulechan[9] = ""
|
||||
rulechan[10] = ""
|
||||
rulechan[11] = ""
|
||||
rulechan[12] = ""
|
||||
rulechan[13] = ""
|
||||
rulechan[14] = ""
|
||||
rulechan[15] = ""
|
||||
clrrule="alarm"
|
||||
clrargs=""
|
||||
rulecolorbg="0"
|
||||
rulecolorfg="0"
|
||||
hdl="0"
|
||||
ldl="0"
|
||||
prec="-1"
|
||||
newunits=""
|
||||
units="append"
|
||||
decorate="none"
|
||||
convertFunc=""
|
||||
convertParams=""
|
||||
}
|
||||
align="horiz. left"
|
||||
format="decimal"
|
||||
}
|
||||
"valuator" {
|
||||
object {
|
||||
x="15"
|
||||
y="43"
|
||||
width="168"
|
||||
height="26"
|
||||
groupid="0"
|
||||
}
|
||||
control {
|
||||
chan="fred"
|
||||
clr="0"
|
||||
bclr="1"
|
||||
label="limits"
|
||||
clrmod="static"
|
||||
rulechan[0] = ""
|
||||
rulechan[1] = ""
|
||||
rulechan[2] = ""
|
||||
rulechan[3] = ""
|
||||
rulechan[4] = ""
|
||||
rulechan[5] = ""
|
||||
rulechan[6] = ""
|
||||
rulechan[7] = ""
|
||||
rulechan[8] = ""
|
||||
rulechan[9] = ""
|
||||
rulechan[10] = ""
|
||||
rulechan[11] = ""
|
||||
rulechan[12] = ""
|
||||
rulechan[13] = ""
|
||||
rulechan[14] = ""
|
||||
rulechan[15] = ""
|
||||
clrrule="alarm"
|
||||
clrargs=""
|
||||
rulecolorbg="0"
|
||||
rulecolorfg="0"
|
||||
hdl="0"
|
||||
ldl="0"
|
||||
prec="-1"
|
||||
newunits=""
|
||||
units="none"
|
||||
decorate="none"
|
||||
convertFunc=""
|
||||
convertParams=""
|
||||
}
|
||||
direction="down"
|
||||
gain="coarse"
|
||||
sendMode="send on motion"
|
||||
increment="0"
|
||||
}
|
||||
"indicator" {
|
||||
object {
|
||||
x="215"
|
||||
y="81"
|
||||
width="170"
|
||||
height="30"
|
||||
groupid="0"
|
||||
}
|
||||
monitor {
|
||||
chan="freddy"
|
||||
clr="0"
|
||||
bclr="1"
|
||||
label="limits"
|
||||
clrmod="static"
|
||||
rulechan[0] = ""
|
||||
rulechan[1] = ""
|
||||
rulechan[2] = ""
|
||||
rulechan[3] = ""
|
||||
rulechan[4] = ""
|
||||
rulechan[5] = ""
|
||||
rulechan[6] = ""
|
||||
rulechan[7] = ""
|
||||
rulechan[8] = ""
|
||||
rulechan[9] = ""
|
||||
rulechan[10] = ""
|
||||
rulechan[11] = ""
|
||||
rulechan[12] = ""
|
||||
rulechan[13] = ""
|
||||
rulechan[14] = ""
|
||||
rulechan[15] = ""
|
||||
clrrule="alarm"
|
||||
clrargs=""
|
||||
rulecolorbg="0"
|
||||
rulecolorfg="0"
|
||||
hdl="0"
|
||||
ldl="0"
|
||||
prec="-1"
|
||||
newunits=""
|
||||
units="none"
|
||||
decorate="none"
|
||||
convertFunc=""
|
||||
convertParams=""
|
||||
}
|
||||
direction="down"
|
||||
RISC_pad="0"
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x="216"
|
||||
y="133"
|
||||
width="171"
|
||||
height="18"
|
||||
groupid="0"
|
||||
}
|
||||
monitor {
|
||||
chan="freddy"
|
||||
clr="0"
|
||||
bclr="1"
|
||||
label="none"
|
||||
clrmod="static"
|
||||
rulechan[0] = ""
|
||||
rulechan[1] = ""
|
||||
rulechan[2] = ""
|
||||
rulechan[3] = ""
|
||||
rulechan[4] = ""
|
||||
rulechan[5] = ""
|
||||
rulechan[6] = ""
|
||||
rulechan[7] = ""
|
||||
rulechan[8] = ""
|
||||
rulechan[9] = ""
|
||||
rulechan[10] = ""
|
||||
rulechan[11] = ""
|
||||
rulechan[12] = ""
|
||||
rulechan[13] = ""
|
||||
rulechan[14] = ""
|
||||
rulechan[15] = ""
|
||||
clrrule="alarm"
|
||||
clrargs=""
|
||||
rulecolorbg="0"
|
||||
rulecolorfg="0"
|
||||
hdl="0"
|
||||
ldl="0"
|
||||
prec="-1"
|
||||
newunits=""
|
||||
units="append"
|
||||
decorate="none"
|
||||
convertFunc=""
|
||||
convertParams=""
|
||||
}
|
||||
align="horiz. left"
|
||||
format="decimal"
|
||||
}
|
||||
"valuator" {
|
||||
object {
|
||||
x="215"
|
||||
y="43"
|
||||
width="168"
|
||||
height="28"
|
||||
groupid="0"
|
||||
}
|
||||
control {
|
||||
chan="freddy"
|
||||
clr="0"
|
||||
bclr="1"
|
||||
label="limits"
|
||||
clrmod="static"
|
||||
rulechan[0] = ""
|
||||
rulechan[1] = ""
|
||||
rulechan[2] = ""
|
||||
rulechan[3] = ""
|
||||
rulechan[4] = ""
|
||||
rulechan[5] = ""
|
||||
rulechan[6] = ""
|
||||
rulechan[7] = ""
|
||||
rulechan[8] = ""
|
||||
rulechan[9] = ""
|
||||
rulechan[10] = ""
|
||||
rulechan[11] = ""
|
||||
rulechan[12] = ""
|
||||
rulechan[13] = ""
|
||||
rulechan[14] = ""
|
||||
rulechan[15] = ""
|
||||
clrrule="alarm"
|
||||
clrargs=""
|
||||
rulecolorbg="0"
|
||||
rulecolorfg="0"
|
||||
hdl="0"
|
||||
ldl="0"
|
||||
prec="-1"
|
||||
newunits=""
|
||||
units="none"
|
||||
decorate="none"
|
||||
convertFunc=""
|
||||
convertParams=""
|
||||
}
|
||||
direction="down"
|
||||
gain="coarse"
|
||||
sendMode="send on motion"
|
||||
increment="0"
|
||||
}
|
||||
"indicator" {
|
||||
object {
|
||||
x="16"
|
||||
y="225"
|
||||
width="171"
|
||||
height="19"
|
||||
groupid="0"
|
||||
}
|
||||
monitor {
|
||||
chan="jane"
|
||||
clr="0"
|
||||
bclr="1"
|
||||
label="limits"
|
||||
clrmod="static"
|
||||
rulechan[0] = ""
|
||||
rulechan[1] = ""
|
||||
rulechan[2] = ""
|
||||
rulechan[3] = ""
|
||||
rulechan[4] = ""
|
||||
rulechan[5] = ""
|
||||
rulechan[6] = ""
|
||||
rulechan[7] = ""
|
||||
rulechan[8] = ""
|
||||
rulechan[9] = ""
|
||||
rulechan[10] = ""
|
||||
rulechan[11] = ""
|
||||
rulechan[12] = ""
|
||||
rulechan[13] = ""
|
||||
rulechan[14] = ""
|
||||
rulechan[15] = ""
|
||||
clrrule="alarm"
|
||||
clrargs=""
|
||||
rulecolorbg="0"
|
||||
rulecolorfg="0"
|
||||
hdl="0"
|
||||
ldl="0"
|
||||
prec="-1"
|
||||
newunits=""
|
||||
units="none"
|
||||
decorate="none"
|
||||
convertFunc=""
|
||||
convertParams=""
|
||||
}
|
||||
direction="down"
|
||||
RISC_pad="0"
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x="17"
|
||||
y="259"
|
||||
width="170"
|
||||
height="20"
|
||||
groupid="0"
|
||||
}
|
||||
monitor {
|
||||
chan="jane"
|
||||
clr="0"
|
||||
bclr="1"
|
||||
label="none"
|
||||
clrmod="static"
|
||||
rulechan[0] = ""
|
||||
rulechan[1] = ""
|
||||
rulechan[2] = ""
|
||||
rulechan[3] = ""
|
||||
rulechan[4] = ""
|
||||
rulechan[5] = ""
|
||||
rulechan[6] = ""
|
||||
rulechan[7] = ""
|
||||
rulechan[8] = ""
|
||||
rulechan[9] = ""
|
||||
rulechan[10] = ""
|
||||
rulechan[11] = ""
|
||||
rulechan[12] = ""
|
||||
rulechan[13] = ""
|
||||
rulechan[14] = ""
|
||||
rulechan[15] = ""
|
||||
clrrule="alarm"
|
||||
clrargs=""
|
||||
rulecolorbg="0"
|
||||
rulecolorfg="0"
|
||||
hdl="0"
|
||||
ldl="0"
|
||||
prec="-1"
|
||||
newunits=""
|
||||
units="append"
|
||||
decorate="none"
|
||||
convertFunc=""
|
||||
convertParams=""
|
||||
}
|
||||
align="horiz. left"
|
||||
format="decimal"
|
||||
}
|
||||
"valuator" {
|
||||
object {
|
||||
x="15"
|
||||
y="187"
|
||||
width="170"
|
||||
height="19"
|
||||
groupid="0"
|
||||
}
|
||||
control {
|
||||
chan="jane"
|
||||
clr="0"
|
||||
bclr="1"
|
||||
label="limits"
|
||||
clrmod="static"
|
||||
rulechan[0] = ""
|
||||
rulechan[1] = ""
|
||||
rulechan[2] = ""
|
||||
rulechan[3] = ""
|
||||
rulechan[4] = ""
|
||||
rulechan[5] = ""
|
||||
rulechan[6] = ""
|
||||
rulechan[7] = ""
|
||||
rulechan[8] = ""
|
||||
rulechan[9] = ""
|
||||
rulechan[10] = ""
|
||||
rulechan[11] = ""
|
||||
rulechan[12] = ""
|
||||
rulechan[13] = ""
|
||||
rulechan[14] = ""
|
||||
rulechan[15] = ""
|
||||
clrrule="alarm"
|
||||
clrargs=""
|
||||
rulecolorbg="0"
|
||||
rulecolorfg="0"
|
||||
hdl="0"
|
||||
ldl="0"
|
||||
prec="-1"
|
||||
newunits=""
|
||||
units="none"
|
||||
decorate="none"
|
||||
convertFunc=""
|
||||
convertParams=""
|
||||
}
|
||||
direction="down"
|
||||
gain="coarse"
|
||||
sendMode="send on motion"
|
||||
increment="0"
|
||||
}
|
||||
"indicator" {
|
||||
object {
|
||||
x="219"
|
||||
y="218"
|
||||
width="173"
|
||||
height="23"
|
||||
groupid="0"
|
||||
}
|
||||
monitor {
|
||||
chan="janet"
|
||||
clr="0"
|
||||
bclr="1"
|
||||
label="limits"
|
||||
clrmod="static"
|
||||
rulechan[0] = ""
|
||||
rulechan[1] = ""
|
||||
rulechan[2] = ""
|
||||
rulechan[3] = ""
|
||||
rulechan[4] = ""
|
||||
rulechan[5] = ""
|
||||
rulechan[6] = ""
|
||||
rulechan[7] = ""
|
||||
rulechan[8] = ""
|
||||
rulechan[9] = ""
|
||||
rulechan[10] = ""
|
||||
rulechan[11] = ""
|
||||
rulechan[12] = ""
|
||||
rulechan[13] = ""
|
||||
rulechan[14] = ""
|
||||
rulechan[15] = ""
|
||||
clrrule="alarm"
|
||||
clrargs=""
|
||||
rulecolorbg="0"
|
||||
rulecolorfg="0"
|
||||
hdl="0"
|
||||
ldl="0"
|
||||
prec="-1"
|
||||
newunits=""
|
||||
units="none"
|
||||
decorate="none"
|
||||
convertFunc=""
|
||||
convertParams=""
|
||||
}
|
||||
direction="down"
|
||||
RISC_pad="0"
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x="220"
|
||||
y="257"
|
||||
width="174"
|
||||
height="20"
|
||||
groupid="0"
|
||||
}
|
||||
monitor {
|
||||
chan="janet"
|
||||
clr="0"
|
||||
bclr="1"
|
||||
label="none"
|
||||
clrmod="static"
|
||||
rulechan[0] = ""
|
||||
rulechan[1] = ""
|
||||
rulechan[2] = ""
|
||||
rulechan[3] = ""
|
||||
rulechan[4] = ""
|
||||
rulechan[5] = ""
|
||||
rulechan[6] = ""
|
||||
rulechan[7] = ""
|
||||
rulechan[8] = ""
|
||||
rulechan[9] = ""
|
||||
rulechan[10] = ""
|
||||
rulechan[11] = ""
|
||||
rulechan[12] = ""
|
||||
rulechan[13] = ""
|
||||
rulechan[14] = ""
|
||||
rulechan[15] = ""
|
||||
clrrule="alarm"
|
||||
clrargs=""
|
||||
rulecolorbg="0"
|
||||
rulecolorfg="0"
|
||||
hdl="0"
|
||||
ldl="0"
|
||||
prec="-1"
|
||||
newunits=""
|
||||
units="append"
|
||||
decorate="none"
|
||||
convertFunc=""
|
||||
convertParams=""
|
||||
}
|
||||
align="horiz. left"
|
||||
format="decimal"
|
||||
}
|
||||
"valuator" {
|
||||
object {
|
||||
x="219"
|
||||
y="188"
|
||||
width="171"
|
||||
height="21"
|
||||
groupid="0"
|
||||
}
|
||||
control {
|
||||
chan="janet"
|
||||
clr="0"
|
||||
bclr="1"
|
||||
label="limits"
|
||||
clrmod="static"
|
||||
rulechan[0] = ""
|
||||
rulechan[1] = ""
|
||||
rulechan[2] = ""
|
||||
rulechan[3] = ""
|
||||
rulechan[4] = ""
|
||||
rulechan[5] = ""
|
||||
rulechan[6] = ""
|
||||
rulechan[7] = ""
|
||||
rulechan[8] = ""
|
||||
rulechan[9] = ""
|
||||
rulechan[10] = ""
|
||||
rulechan[11] = ""
|
||||
rulechan[12] = ""
|
||||
rulechan[13] = ""
|
||||
rulechan[14] = ""
|
||||
rulechan[15] = ""
|
||||
clrrule="alarm"
|
||||
clrargs=""
|
||||
rulecolorbg="0"
|
||||
rulecolorfg="0"
|
||||
hdl="0"
|
||||
ldl="0"
|
||||
prec="-1"
|
||||
newunits=""
|
||||
units="none"
|
||||
decorate="none"
|
||||
convertFunc=""
|
||||
convertParams=""
|
||||
}
|
||||
direction="down"
|
||||
gain="coarse"
|
||||
sendMode="send on motion"
|
||||
increment="0"
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
//
|
||||
// Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd
|
||||
// Author: Jeff HIll (LANL)
|
||||
//
|
||||
//
|
||||
|
||||
#include <vxWorks.h>
|
||||
#include <taskLib.h>
|
||||
|
||||
#include "exServer.h"
|
||||
|
||||
//
|
||||
// so we can call this from the vxWorks shell
|
||||
//
|
||||
extern "C" {
|
||||
|
||||
exServer *pExampleCAS;
|
||||
|
||||
//
|
||||
// excas ()
|
||||
// (vxWorks example server entry point)
|
||||
//
|
||||
int excas (unsigned debugLevel, unsigned delaySec)
|
||||
{
|
||||
epicsTime begin(epicsTime::getCurrent());
|
||||
exServer *pCAS;
|
||||
|
||||
pCAS = new exServer(32u,5u,500u);
|
||||
if (!pCAS) {
|
||||
return (-1);
|
||||
}
|
||||
|
||||
pCAS->setDebugLevel(debugLevel);
|
||||
pExampleCAS = pCAS;
|
||||
|
||||
if (delaySec==0u) {
|
||||
//
|
||||
// loop here forever
|
||||
//
|
||||
while (1) {
|
||||
taskDelay(10);
|
||||
}
|
||||
}
|
||||
else {
|
||||
epicsTime total( ((float)delaySec) );
|
||||
epicsTime delay(epicsTime::getCurrent() - begin);
|
||||
//
|
||||
// loop here untill the specified execution time
|
||||
// expires
|
||||
//
|
||||
while (delay < total) {
|
||||
taskDelay(10);
|
||||
delay = epicsTime::getCurrent() - begin;
|
||||
}
|
||||
}
|
||||
pCAS->show(debugLevel);
|
||||
pExampleCAS = NULL;
|
||||
delete pCAS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int excasShow(unsigned level)
|
||||
{
|
||||
if (pExampleCAS!=NULL) {
|
||||
pExampleCAS->show(level);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# CONFIG - Load build configuration data
|
||||
#
|
||||
# Do not make changes to this file!
|
||||
|
||||
# Allow user to override where the build rules come from
|
||||
RULES = $(EPICS_BASE)
|
||||
|
||||
# RELEASE files point to other application tops
|
||||
include $(TOP)/configure/RELEASE
|
||||
-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).Common
|
||||
ifdef T_A
|
||||
-include $(TOP)/configure/RELEASE.Common.$(T_A)
|
||||
-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A)
|
||||
endif
|
||||
|
||||
CONFIG = $(RULES)/configure
|
||||
include $(CONFIG)/CONFIG
|
||||
|
||||
# Override the Base definition:
|
||||
INSTALL_LOCATION = $(TOP)
|
||||
|
||||
# CONFIG_SITE files contain other build configuration settings
|
||||
include $(TOP)/configure/CONFIG_SITE
|
||||
-include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).Common
|
||||
ifdef T_A
|
||||
-include $(TOP)/configure/CONFIG_SITE.Common.$(T_A)
|
||||
-include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A)
|
||||
endif
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# CONFIG_SITE
|
||||
|
||||
# Make any application-specific changes to the EPICS build
|
||||
# configuration variables in this file.
|
||||
#
|
||||
# Host/target specific settings can be specified in files named
|
||||
# CONFIG_SITE.$(EPICS_HOST_ARCH).Common
|
||||
# CONFIG_SITE.Common.$(T_A)
|
||||
# CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A)
|
||||
|
||||
# CHECK_RELEASE controls the consistency checking of the support
|
||||
# applications pointed to by the RELEASE* files.
|
||||
# Normally CHECK_RELEASE should be set to YES.
|
||||
# Set CHECK_RELEASE to NO to disable checking completely.
|
||||
# Set CHECK_RELEASE to WARN to perform consistency checking but
|
||||
# continue building anyway if conflicts are found.
|
||||
CHECK_RELEASE = YES
|
||||
|
||||
# Set this when you only want to compile this application
|
||||
# for a subset of the cross-compiled target architectures
|
||||
# that Base is built for.
|
||||
#CROSS_COMPILER_TARGET_ARCHS = vxWorks-68040
|
||||
|
||||
# To install files into a location other than $(TOP) define
|
||||
# INSTALL_LOCATION here.
|
||||
#INSTALL_LOCATION=</path/name/to/install/top>
|
||||
|
||||
# Set this when your IOC and the host use different paths
|
||||
# to access the application. This will be needed to boot
|
||||
# from a Microsoft FTP server or with some NFS mounts.
|
||||
# You must rebuild in the iocBoot directory for this to
|
||||
# take effect.
|
||||
#IOCS_APPL_TOP = </IOC/path/to/application/top>
|
||||
@@ -0,0 +1,8 @@
|
||||
TOP=..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
TARGETS = $(CONFIG_TARGETS)
|
||||
CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS)))
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
@@ -0,0 +1,32 @@
|
||||
# RELEASE - Location of external support modules
|
||||
#
|
||||
# IF YOU MAKE ANY CHANGES to this file you must subsequently
|
||||
# do a "gnumake rebuild" in this application's top level
|
||||
# directory.
|
||||
#
|
||||
# The build process does not check dependencies against files
|
||||
# that are outside this application, thus you should do a
|
||||
# "gnumake rebuild" in the top level directory after EPICS_BASE
|
||||
# or any other external module pointed to below is rebuilt.
|
||||
#
|
||||
# Host- or target-specific settings can be given in files named
|
||||
# RELEASE.$(EPICS_HOST_ARCH).Common
|
||||
# RELEASE.Common.$(T_A)
|
||||
# RELEASE.$(EPICS_HOST_ARCH).$(T_A)
|
||||
#
|
||||
# This file should ONLY define paths to other support modules,
|
||||
# or include statements that pull in similar RELEASE files.
|
||||
# Build settings that are NOT module paths should appear in a
|
||||
# CONFIG_SITE file.
|
||||
|
||||
TEMPLATE_TOP=_TEMPLATE_TOP_
|
||||
|
||||
# If using the sequencer, point SNCSEQ at its top directory:
|
||||
#SNCSEQ=$(EPICS_BASE)/../modules/soft/seq
|
||||
|
||||
# EPICS_BASE usually appears last so other apps can override stuff:
|
||||
EPICS_BASE=_EPICS_BASE_
|
||||
|
||||
# Set RULES here if you want to take build rules from somewhere
|
||||
# other than EPICS_BASE:
|
||||
#RULES=/path/to/epics/support/module/rules/x-y
|
||||
@@ -0,0 +1,6 @@
|
||||
# RULES
|
||||
|
||||
include $(CONFIG)/RULES
|
||||
|
||||
# Library should be rebuilt because LIBOBJS may have changed.
|
||||
$(LIBNAME): ../Makefile
|
||||
@@ -0,0 +1,2 @@
|
||||
#RULES.ioc
|
||||
include $(CONFIG)/RULES.ioc
|
||||
@@ -0,0 +1,2 @@
|
||||
#RULES_DIRS
|
||||
include $(CONFIG)/RULES_DIRS
|
||||
@@ -0,0 +1,3 @@
|
||||
#RULES_TOP
|
||||
include $(CONFIG)/RULES_TOP
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
TOP=../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
#----------------------------------------
|
||||
# ADD MACRO DEFINITIONS AFTER THIS LINE
|
||||
|
||||
#----------------------------------------------------
|
||||
# Optimization of db files using dbst (DEFAULT: NO)
|
||||
#DB_OPT = YES
|
||||
|
||||
#----------------------------------------------------
|
||||
# Create and install (or just install)
|
||||
# databases, templates, substitutions like this
|
||||
DB += dbExample1.db
|
||||
DB += dbExample2.db
|
||||
DB += dbSubExample.db
|
||||
DB += user.substitutions
|
||||
DB += userHost.substitutions
|
||||
|
||||
#----------------------------------------------------
|
||||
# If <anyname>.db template is not named <anyname>*.template add
|
||||
# <anyname>_TEMPLATE = <templatename>
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
#----------------------------------------
|
||||
# ADD RULES AFTER THIS LINE
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
record(ai, "$(user):aiExample")
|
||||
{
|
||||
field(DESC, "Analog input")
|
||||
field(INP, "$(user):calcExample.VAL NPP NMS")
|
||||
field(EGUF, "10")
|
||||
field(EGU, "Counts")
|
||||
field(HOPR, "10")
|
||||
field(LOPR, "0")
|
||||
field(HIHI, "8")
|
||||
field(HIGH, "6")
|
||||
field(LOW, "4")
|
||||
field(LOLO, "2")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HSV, "MINOR")
|
||||
field(LSV, "MINOR")
|
||||
field(LLSV, "MAJOR")
|
||||
}
|
||||
record(calc, "$(user):calcExample")
|
||||
{
|
||||
field(DESC, "Counter")
|
||||
field(SCAN,"1 second")
|
||||
field(FLNK, "$(user):aiExample")
|
||||
field(CALC, "(A<B)?(A+C):D")
|
||||
field(INPA, "$(user):calcExample.VAL NPP NMS")
|
||||
field(INPB, "9")
|
||||
field(INPC, "1")
|
||||
field(INPD, "0")
|
||||
field(EGU, "Counts")
|
||||
field(HOPR, "10")
|
||||
field(HIHI, "8")
|
||||
field(HIGH, "6")
|
||||
field(LOW, "4")
|
||||
field(LOLO, "2")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HSV, "MINOR")
|
||||
field(LSV, "MINOR")
|
||||
field(LLSV, "MAJOR")
|
||||
}
|
||||
record(xxx, "$(user):xxxExample")
|
||||
{
|
||||
field(DESC, "xxx record")
|
||||
field(EGU, "Counts")
|
||||
field(HOPR, "10")
|
||||
field(HIHI, "8")
|
||||
field(HIGH, "6")
|
||||
field(LOW, "4")
|
||||
field(LOLO, "2")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HSV, "MINOR")
|
||||
field(LSV, "MINOR")
|
||||
field(LLSV, "MAJOR")
|
||||
}
|
||||
record(compress,"$(user):compressExample")
|
||||
{
|
||||
field(DESC, "Circular buffer")
|
||||
field(INP,"$(user):aiExample.VAL CP NMS")
|
||||
field(ALG,"Circular Buffer")
|
||||
field(NSAM,"10")
|
||||
field(HOPR,"10")
|
||||
field(EGU,"Counts")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
record(calc, "$(user):calcExample$(no)")
|
||||
{
|
||||
alias("$(user):calc$(no)")
|
||||
field(DESC, "Counter No. $(no)")
|
||||
field(SCAN,"$(scan)")
|
||||
field(FLNK, "$(user):aiExample$(no)")
|
||||
field(CALC, "(A<B)?(A+C):D")
|
||||
field(INPA, "$(user):calcExample$(no).VAL NPP NMS")
|
||||
field(INPB, "9")
|
||||
field(INPC, "1")
|
||||
field(INPD, "0")
|
||||
field(EGU, "Counts")
|
||||
field(HOPR, "10")
|
||||
field(HIHI, "8")
|
||||
field(HIGH, "6")
|
||||
field(LOW, "4")
|
||||
field(LOLO, "2")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HSV, "MINOR")
|
||||
field(LSV, "MINOR")
|
||||
field(LLSV, "MAJOR")
|
||||
}
|
||||
record(ai, "$(user):aiExample$(no)")
|
||||
{
|
||||
field(DESC, "Analog input No. $(no)")
|
||||
field(INP, "$(user):calcExample$(no).VAL NPP NMS")
|
||||
field(EGUF, "10")
|
||||
field(EGU, "Counts")
|
||||
field(HOPR, "10")
|
||||
field(LOPR, "0")
|
||||
field(HIHI, "8")
|
||||
field(HIGH, "6")
|
||||
field(LOW, "4")
|
||||
field(LOLO, "2")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HSV, "MINOR")
|
||||
field(LSV, "MINOR")
|
||||
field(LLSV, "MAJOR")
|
||||
}
|
||||
alias("$(user):aiExample$(no)","$(user):ai$(no)")
|
||||
@@ -0,0 +1,13 @@
|
||||
record(sub,"$(user):subExample")
|
||||
{
|
||||
field(INAM,"mySubInit")
|
||||
field(SNAM,"mySubProcess")
|
||||
}
|
||||
record(aSub,"$(user):aSubExample")
|
||||
{
|
||||
field(INAM,"myAsubInit")
|
||||
field(SNAM,"myAsubProcess")
|
||||
field(FTA,"DOUBLE")
|
||||
field(NOA,"10")
|
||||
field(INPA,"$(user):compressExample CPP")
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
# Example substitutions file
|
||||
|
||||
file "db/dbExample1.db" {
|
||||
{ user = "_USER_" }
|
||||
}
|
||||
|
||||
file db/dbExample2.db {
|
||||
pattern { user, no, scan }
|
||||
{ "_USER_", 1, "1 second" }
|
||||
{ "_USER_", 2, "2 second" }
|
||||
{ "_USER_", 3, "5 second" }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
# Example substitutions file
|
||||
|
||||
file "db/dbExample1.db" {
|
||||
{ user = "_USER_Host" }
|
||||
}
|
||||
|
||||
file db/dbExample2.db {
|
||||
pattern { user, no, scan }
|
||||
{ "_USER_Host", 1, "1 second" }
|
||||
{ "_USER_Host", 2, "2 second" }
|
||||
{ "_USER_Host", 3, "5 second" }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
TOP = ..
|
||||
include $(TOP)/configure/CONFIG
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *src*))
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Src*))
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *db*))
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Db*))
|
||||
include $(TOP)/configure/RULES_DIRS
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
TOP=../..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
#----------------------------------------
|
||||
# ADD MACRO DEFINITIONS AFTER THIS LINE
|
||||
|
||||
#=============================
|
||||
# Build the IOC support library
|
||||
|
||||
LIBRARY_IOC += _APPNAME_Support
|
||||
|
||||
# xxxRecord.h will be created from xxxRecord.dbd
|
||||
DBDINC += xxxRecord
|
||||
# Install devXxxSoft.dbd into <top>/dbd
|
||||
DBD += xxxSupport.dbd
|
||||
|
||||
# Compile and add the code to the support library
|
||||
_APPNAME_Support_SRCS += xxxRecord.c
|
||||
_APPNAME_Support_SRCS += devXxxSoft.c
|
||||
|
||||
# Link locally-provided code into the support library,
|
||||
# rather than directly into the IOC application.
|
||||
_APPNAME_Support_SRCS += dbSubExample.c
|
||||
_APPNAME_Support_SRCS += _APPNAME_Hello.c
|
||||
_APPNAME_Support_SRCS += initTrace.c
|
||||
|
||||
_APPNAME_Support_LIBS += $(EPICS_BASE_IOC_LIBS)
|
||||
|
||||
#=============================
|
||||
# Build the IOC application
|
||||
|
||||
PROD_IOC = _APPNAME_
|
||||
# _APPNAME_.dbd will be created and installed
|
||||
DBD += _APPNAME_.dbd
|
||||
|
||||
# _APPNAME_.dbd will be made up from these files:
|
||||
_APPNAME__DBD += base.dbd
|
||||
_APPNAME__DBD += xxxSupport.dbd
|
||||
_APPNAME__DBD += dbSubExample.dbd
|
||||
_APPNAME__DBD += _APPNAME_Hello.dbd
|
||||
_APPNAME__DBD += initTrace.dbd
|
||||
|
||||
# _APPNAME__registerRecordDeviceDriver.cpp derives from _APPNAME_.dbd
|
||||
_APPNAME__SRCS += _APPNAME__registerRecordDeviceDriver.cpp
|
||||
|
||||
# Build the main IOC entry point on workstation OSs.
|
||||
_APPNAME__SRCS_DEFAULT += _APPNAME_Main.cpp
|
||||
_APPNAME__SRCS_vxWorks += -nil-
|
||||
|
||||
# Add support from base/src/vxWorks if needed
|
||||
#_APPNAME__OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary
|
||||
|
||||
# Link in the code from the support library
|
||||
_APPNAME__LIBS += _APPNAME_Support
|
||||
|
||||
# NOTE: To build SNL programs, SNCSEQ must be defined
|
||||
# in the <top>/configure/RELEASE file
|
||||
|
||||
ifneq ($(SNCSEQ),)
|
||||
# Build sncExample into _APPNAME_Support
|
||||
sncExample_SNCFLAGS += +r
|
||||
_APPNAME__DBD += sncExample.dbd
|
||||
_APPNAME_Support_SRCS += sncExample.stt
|
||||
_APPNAME_Support_LIBS += seq pv
|
||||
_APPNAME__LIBS += seq pv
|
||||
|
||||
# Build sncProgram as a standalone program
|
||||
PROD_HOST += sncProgram
|
||||
sncProgram_SNCFLAGS += +m
|
||||
sncProgram_SRCS += sncProgram.st
|
||||
sncProgram_LIBS += seq pv
|
||||
sncProgram_LIBS += $(EPICS_BASE_HOST_LIBS)
|
||||
endif
|
||||
|
||||
# Finally link to the EPICS Base libraries
|
||||
_APPNAME__LIBS += $(EPICS_BASE_IOC_LIBS)
|
||||
|
||||
#=============================
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
#----------------------------------------
|
||||
# ADD RULES AFTER THIS LINE
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/* Example showing how to register a new command with iocsh */
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <epicsExport.h>
|
||||
#include <iocsh.h>
|
||||
|
||||
/* This is the command, which the vxWorks shell will call directly */
|
||||
void hello(const char *name) {
|
||||
if (name) {
|
||||
printf("Hello %s, from _APPNAME_\n", name);
|
||||
} else {
|
||||
puts("Hello from _APPNAME_");
|
||||
}
|
||||
}
|
||||
|
||||
/* Information needed by iocsh */
|
||||
static const iocshArg helloArg0 = {"name", iocshArgString};
|
||||
static const iocshArg *helloArgs[] = {&helloArg0};
|
||||
static const iocshFuncDef helloFuncDef = {"hello", 1, helloArgs};
|
||||
|
||||
/* Wrapper called by iocsh, selects the argument types that hello needs */
|
||||
static void helloCallFunc(const iocshArgBuf *args) {
|
||||
hello(args[0].sval);
|
||||
}
|
||||
|
||||
/* Registration routine, runs at startup */
|
||||
static void helloRegister(void) {
|
||||
iocshRegister(&helloFuncDef, helloCallFunc);
|
||||
}
|
||||
epicsExportRegistrar(helloRegister);
|
||||
@@ -0,0 +1 @@
|
||||
registrar(helloRegister)
|
||||
@@ -0,0 +1,23 @@
|
||||
/* _APPNAME_Main.cpp */
|
||||
/* Author: Marty Kraimer Date: 17MAR2000 */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "epicsExit.h"
|
||||
#include "epicsThread.h"
|
||||
#include "iocsh.h"
|
||||
|
||||
int main(int argc,char *argv[])
|
||||
{
|
||||
if(argc>=2) {
|
||||
iocsh(argv[1]);
|
||||
epicsThreadSleep(.2);
|
||||
}
|
||||
iocsh(NULL);
|
||||
epicsExit(0);
|
||||
return(0);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include <dbDefs.h>
|
||||
#include <registryFunction.h>
|
||||
#include <subRecord.h>
|
||||
#include <aSubRecord.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
int mySubDebug;
|
||||
|
||||
static long mySubInit(subRecord *precord)
|
||||
{
|
||||
if (mySubDebug)
|
||||
printf("Record %s called mySubInit(%p)\n",
|
||||
precord->name, (void*) precord);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long mySubProcess(subRecord *precord)
|
||||
{
|
||||
if (mySubDebug)
|
||||
printf("Record %s called mySubProcess(%p)\n",
|
||||
precord->name, (void*) precord);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long myAsubInit(aSubRecord *precord)
|
||||
{
|
||||
if (mySubDebug)
|
||||
printf("Record %s called myAsubInit(%p)\n",
|
||||
precord->name, (void*) precord);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long myAsubProcess(aSubRecord *precord)
|
||||
{
|
||||
if (mySubDebug)
|
||||
printf("Record %s called myAsubProcess(%p)\n",
|
||||
precord->name, (void*) precord);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Register these symbols for use by IOC code: */
|
||||
|
||||
epicsExportAddress(int, mySubDebug);
|
||||
epicsRegisterFunction(mySubInit);
|
||||
epicsRegisterFunction(mySubProcess);
|
||||
epicsRegisterFunction(myAsubInit);
|
||||
epicsRegisterFunction(myAsubProcess);
|
||||
@@ -0,0 +1,5 @@
|
||||
variable(mySubDebug)
|
||||
function(mySubInit)
|
||||
function(mySubProcess)
|
||||
function(myAsubInit)
|
||||
function(myAsubProcess)
|
||||
@@ -0,0 +1,58 @@
|
||||
/* devXxxSoft.c */
|
||||
/* Example device support module */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alarm.h"
|
||||
#include "cvtTable.h"
|
||||
#include "dbDefs.h"
|
||||
#include "dbAccess.h"
|
||||
#include "recGbl.h"
|
||||
#include "recSup.h"
|
||||
#include "devSup.h"
|
||||
#include "link.h"
|
||||
#include "xxxRecord.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
/*Create the dset for devXxxSoft */
|
||||
static long init_record();
|
||||
static long read_xxx();
|
||||
struct {
|
||||
long number;
|
||||
DEVSUPFUN report;
|
||||
DEVSUPFUN init;
|
||||
DEVSUPFUN init_record;
|
||||
DEVSUPFUN get_ioint_info;
|
||||
DEVSUPFUN read_xxx;
|
||||
}devXxxSoft={
|
||||
5,
|
||||
NULL,
|
||||
NULL,
|
||||
init_record,
|
||||
NULL,
|
||||
read_xxx,
|
||||
};
|
||||
epicsExportAddress(dset,devXxxSoft);
|
||||
|
||||
|
||||
static long init_record(pxxx)
|
||||
struct xxxRecord *pxxx;
|
||||
{
|
||||
if(recGblInitConstantLink(&pxxx->inp,DBF_DOUBLE,&pxxx->val))
|
||||
pxxx->udf = FALSE;
|
||||
return(0);
|
||||
}
|
||||
|
||||
static long read_xxx(pxxx)
|
||||
struct xxxRecord *pxxx;
|
||||
{
|
||||
long status;
|
||||
|
||||
status = dbGetLink(&(pxxx->inp),DBF_DOUBLE, &(pxxx->val),0,0);
|
||||
/*If return was succesful then set undefined false*/
|
||||
if(!status) pxxx->udf = FALSE;
|
||||
return(0);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/* initTrace.c */
|
||||
|
||||
/*
|
||||
* An initHook routine to trace the iocInit() process.
|
||||
* Prints out the name of each state as it is reached.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "initHooks.h"
|
||||
#include "epicsExport.h"
|
||||
#include "iocsh.h"
|
||||
|
||||
|
||||
static void trace(initHookState state) {
|
||||
printf("iocInit: Reached %s\n", initHookName(state));
|
||||
}
|
||||
|
||||
int traceIocInit(void) {
|
||||
static int done = 0;
|
||||
if (done)
|
||||
return -1;
|
||||
done = 1;
|
||||
|
||||
initHookRegister(trace);
|
||||
puts("iocInit will be traced");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const iocshFuncDef traceInitFuncDef = {"traceIocInit", 0, NULL};
|
||||
static void traceInitFunc(const iocshArgBuf *args) {
|
||||
traceIocInit();
|
||||
}
|
||||
|
||||
static void initTraceRegister(void) {
|
||||
iocshRegister(&traceInitFuncDef, traceInitFunc);
|
||||
}
|
||||
epicsExportRegistrar(initTraceRegister);
|
||||
@@ -0,0 +1 @@
|
||||
registrar(initTraceRegister)
|
||||
@@ -0,0 +1 @@
|
||||
registrar(sncExampleRegistrar)
|
||||
@@ -0,0 +1,22 @@
|
||||
program sncExample
|
||||
double v;
|
||||
assign v to "{user}:aiExample";
|
||||
monitor v;
|
||||
|
||||
ss ss1 {
|
||||
state init {
|
||||
when (delay(10)) {
|
||||
printf("sncExample: Startup delay over\n");
|
||||
} state low
|
||||
}
|
||||
state low {
|
||||
when (v > 5.0) {
|
||||
printf("sncExample: Changing to high\n");
|
||||
} state high
|
||||
}
|
||||
state high {
|
||||
when (v <= 5.0) {
|
||||
printf("sncExample: Changing to low\n");
|
||||
} state low
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#include "../sncExample.stt"
|
||||
@@ -0,0 +1,273 @@
|
||||
/* xxxRecord.c */
|
||||
/* Example record support module */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "epicsMath.h"
|
||||
#include "alarm.h"
|
||||
#include "dbAccess.h"
|
||||
#include "recGbl.h"
|
||||
#include "dbEvent.h"
|
||||
#include "dbDefs.h"
|
||||
#include "dbAccess.h"
|
||||
#include "devSup.h"
|
||||
#include "errMdef.h"
|
||||
#include "recSup.h"
|
||||
#include "special.h"
|
||||
#define GEN_SIZE_OFFSET
|
||||
#include "xxxRecord.h"
|
||||
#undef GEN_SIZE_OFFSET
|
||||
#include "epicsExport.h"
|
||||
|
||||
/* Create RSET - Record Support Entry Table */
|
||||
#define report NULL
|
||||
#define initialize NULL
|
||||
static long init_record();
|
||||
static long process();
|
||||
#define special NULL
|
||||
#define get_value NULL
|
||||
#define cvt_dbaddr NULL
|
||||
#define get_array_info NULL
|
||||
#define put_array_info NULL
|
||||
static long get_units();
|
||||
static long get_precision();
|
||||
#define get_enum_str NULL
|
||||
#define get_enum_strs NULL
|
||||
#define put_enum_str NULL
|
||||
static long get_graphic_double();
|
||||
static long get_control_double();
|
||||
static long get_alarm_double();
|
||||
|
||||
rset xxxRSET={
|
||||
RSETNUMBER,
|
||||
report,
|
||||
initialize,
|
||||
init_record,
|
||||
process,
|
||||
special,
|
||||
get_value,
|
||||
cvt_dbaddr,
|
||||
get_array_info,
|
||||
put_array_info,
|
||||
get_units,
|
||||
get_precision,
|
||||
get_enum_str,
|
||||
get_enum_strs,
|
||||
put_enum_str,
|
||||
get_graphic_double,
|
||||
get_control_double,
|
||||
get_alarm_double
|
||||
};
|
||||
epicsExportAddress(rset,xxxRSET);
|
||||
|
||||
typedef struct xxxset { /* xxx input dset */
|
||||
long number;
|
||||
DEVSUPFUN dev_report;
|
||||
DEVSUPFUN init;
|
||||
DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/
|
||||
DEVSUPFUN get_ioint_info;
|
||||
DEVSUPFUN read_xxx;
|
||||
}xxxdset;
|
||||
|
||||
static void checkAlarms(xxxRecord *prec);
|
||||
static void monitor(xxxRecord *prec);
|
||||
|
||||
static long init_record(void *precord,int pass)
|
||||
{
|
||||
xxxRecord *prec = (xxxRecord *)precord;
|
||||
xxxdset *pdset;
|
||||
long status;
|
||||
|
||||
if (pass==0) return(0);
|
||||
|
||||
if(!(pdset = (xxxdset *)(prec->dset))) {
|
||||
recGblRecordError(S_dev_noDSET,(void *)prec,"xxx: init_record");
|
||||
return(S_dev_noDSET);
|
||||
}
|
||||
/* must have read_xxx function defined */
|
||||
if( (pdset->number < 5) || (pdset->read_xxx == NULL) ) {
|
||||
recGblRecordError(S_dev_missingSup,(void *)prec,"xxx: init_record");
|
||||
return(S_dev_missingSup);
|
||||
}
|
||||
|
||||
if( pdset->init_record ) {
|
||||
if((status=(*pdset->init_record)(prec))) return(status);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
static long process(void *precord)
|
||||
{
|
||||
xxxRecord *prec = (xxxRecord *)precord;
|
||||
xxxdset *pdset = (xxxdset *)(prec->dset);
|
||||
long status;
|
||||
unsigned char pact=prec->pact;
|
||||
|
||||
if( (pdset==NULL) || (pdset->read_xxx==NULL) ) {
|
||||
prec->pact=TRUE;
|
||||
recGblRecordError(S_dev_missingSup,(void *)prec,"read_xxx");
|
||||
return(S_dev_missingSup);
|
||||
}
|
||||
|
||||
/* pact must not be set until after calling device support */
|
||||
status=(*pdset->read_xxx)(prec);
|
||||
/* check if device support set pact */
|
||||
if ( !pact && prec->pact ) return(0);
|
||||
prec->pact = TRUE;
|
||||
|
||||
recGblGetTimeStamp(prec);
|
||||
/* check for alarms */
|
||||
checkAlarms(prec);
|
||||
/* check event list */
|
||||
monitor(prec);
|
||||
/* process the forward scan link record */
|
||||
recGblFwdLink(prec);
|
||||
|
||||
prec->pact=FALSE;
|
||||
return(status);
|
||||
}
|
||||
|
||||
static long get_units(DBADDR *paddr, char *units)
|
||||
{
|
||||
xxxRecord *prec=(xxxRecord *)paddr->precord;
|
||||
|
||||
strncpy(units,prec->egu,DB_UNITS_SIZE);
|
||||
return(0);
|
||||
}
|
||||
|
||||
static long get_precision(DBADDR *paddr, long *precision)
|
||||
{
|
||||
xxxRecord *prec=(xxxRecord *)paddr->precord;
|
||||
|
||||
*precision = prec->prec;
|
||||
if(paddr->pfield == (void *)&prec->val) return(0);
|
||||
recGblGetPrec(paddr,precision);
|
||||
return(0);
|
||||
}
|
||||
|
||||
static long get_graphic_double(DBADDR *paddr,struct dbr_grDouble *pgd)
|
||||
{
|
||||
xxxRecord *prec=(xxxRecord *)paddr->precord;
|
||||
int fieldIndex = dbGetFieldIndex(paddr);
|
||||
|
||||
if(fieldIndex == xxxRecordVAL
|
||||
|| fieldIndex == xxxRecordHIHI
|
||||
|| fieldIndex == xxxRecordHIGH
|
||||
|| fieldIndex == xxxRecordLOW
|
||||
|| fieldIndex == xxxRecordLOLO
|
||||
|| fieldIndex == xxxRecordHOPR
|
||||
|| fieldIndex == xxxRecordLOPR) {
|
||||
pgd->upper_disp_limit = prec->hopr;
|
||||
pgd->lower_disp_limit = prec->lopr;
|
||||
} else recGblGetGraphicDouble(paddr,pgd);
|
||||
return(0);
|
||||
}
|
||||
|
||||
static long get_control_double(DBADDR *paddr,struct dbr_ctrlDouble *pcd)
|
||||
{
|
||||
xxxRecord *prec=(xxxRecord *)paddr->precord;
|
||||
int fieldIndex = dbGetFieldIndex(paddr);
|
||||
|
||||
if(fieldIndex == xxxRecordVAL
|
||||
|| fieldIndex == xxxRecordHIHI
|
||||
|| fieldIndex == xxxRecordHIGH
|
||||
|| fieldIndex == xxxRecordLOW
|
||||
|| fieldIndex == xxxRecordLOLO) {
|
||||
pcd->upper_ctrl_limit = prec->hopr;
|
||||
pcd->lower_ctrl_limit = prec->lopr;
|
||||
} else recGblGetControlDouble(paddr,pcd);
|
||||
return(0);
|
||||
}
|
||||
|
||||
static long get_alarm_double(DBADDR *paddr,struct dbr_alDouble *pad)
|
||||
{
|
||||
xxxRecord *prec=(xxxRecord *)paddr->precord;
|
||||
int fieldIndex = dbGetFieldIndex(paddr);
|
||||
|
||||
if(fieldIndex == xxxRecordVAL) {
|
||||
pad->upper_alarm_limit = prec->hhsv ? prec->hihi : epicsNAN;
|
||||
pad->upper_warning_limit = prec->hsv ? prec->high : epicsNAN;
|
||||
pad->lower_warning_limit = prec->lsv ? prec->low : epicsNAN;
|
||||
pad->lower_alarm_limit = prec->llsv ? prec->lolo : epicsNAN;
|
||||
} else recGblGetAlarmDouble(paddr,pad);
|
||||
return(0);
|
||||
}
|
||||
|
||||
static void checkAlarms(xxxRecord *prec)
|
||||
{
|
||||
double val;
|
||||
float hyst, lalm, hihi, high, low, lolo;
|
||||
unsigned short hhsv, llsv, hsv, lsv;
|
||||
|
||||
if(prec->udf == TRUE ){
|
||||
recGblSetSevr(prec,UDF_ALARM,INVALID_ALARM);
|
||||
return;
|
||||
}
|
||||
hihi = prec->hihi; lolo = prec->lolo; high = prec->high; low = prec->low;
|
||||
hhsv = prec->hhsv; llsv = prec->llsv; hsv = prec->hsv; lsv = prec->lsv;
|
||||
val = prec->val; hyst = prec->hyst; lalm = prec->lalm;
|
||||
|
||||
/* alarm condition hihi */
|
||||
if (hhsv && (val >= hihi || ((lalm==hihi) && (val >= hihi-hyst)))){
|
||||
if (recGblSetSevr(prec,HIHI_ALARM,prec->hhsv)) prec->lalm = hihi;
|
||||
return;
|
||||
}
|
||||
|
||||
/* alarm condition lolo */
|
||||
if (llsv && (val <= lolo || ((lalm==lolo) && (val <= lolo+hyst)))){
|
||||
if (recGblSetSevr(prec,LOLO_ALARM,prec->llsv)) prec->lalm = lolo;
|
||||
return;
|
||||
}
|
||||
|
||||
/* alarm condition high */
|
||||
if (hsv && (val >= high || ((lalm==high) && (val >= high-hyst)))){
|
||||
if (recGblSetSevr(prec,HIGH_ALARM,prec->hsv)) prec->lalm = high;
|
||||
return;
|
||||
}
|
||||
|
||||
/* alarm condition low */
|
||||
if (lsv && (val <= low || ((lalm==low) && (val <= low+hyst)))){
|
||||
if (recGblSetSevr(prec,LOW_ALARM,prec->lsv)) prec->lalm = low;
|
||||
return;
|
||||
}
|
||||
|
||||
/* we get here only if val is out of alarm by at least hyst */
|
||||
prec->lalm = val;
|
||||
return;
|
||||
}
|
||||
|
||||
static void monitor(xxxRecord *prec)
|
||||
{
|
||||
unsigned short monitor_mask;
|
||||
double delta;
|
||||
|
||||
monitor_mask = recGblResetAlarms(prec);
|
||||
/* check for value change */
|
||||
delta = prec->mlst - prec->val;
|
||||
if(delta<0.0) delta = -delta;
|
||||
if (delta > prec->mdel) {
|
||||
/* post events for value change */
|
||||
monitor_mask |= DBE_VALUE;
|
||||
/* update last value monitored */
|
||||
prec->mlst = prec->val;
|
||||
}
|
||||
|
||||
/* check for archive change */
|
||||
delta = prec->alst - prec->val;
|
||||
if(delta<0.0) delta = -delta;
|
||||
if (delta > prec->adel) {
|
||||
/* post events on value field for archive change */
|
||||
monitor_mask |= DBE_LOG;
|
||||
/* update last archive value monitored */
|
||||
prec->alst = prec->val;
|
||||
}
|
||||
|
||||
/* send out monitors connected to the value field */
|
||||
if (monitor_mask){
|
||||
db_post_events(prec,&prec->val,monitor_mask);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
recordtype(xxx) {
|
||||
include "dbCommon.dbd"
|
||||
field(VAL,DBF_DOUBLE) {
|
||||
prompt("Current EGU Value")
|
||||
asl(ASL0)
|
||||
pp(TRUE)
|
||||
}
|
||||
field(INP,DBF_INLINK) {
|
||||
prompt("Input Specification")
|
||||
promptgroup(GUI_INPUTS)
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
}
|
||||
field(PREC,DBF_SHORT) {
|
||||
prompt("Display Precision")
|
||||
promptgroup(GUI_DISPLAY)
|
||||
interest(1)
|
||||
}
|
||||
field(EGU,DBF_STRING) {
|
||||
prompt("Engineering Units")
|
||||
promptgroup(GUI_DISPLAY)
|
||||
interest(1)
|
||||
size(16)
|
||||
}
|
||||
field(HOPR,DBF_FLOAT) {
|
||||
prompt("High Operating Range")
|
||||
promptgroup(GUI_DISPLAY)
|
||||
interest(1)
|
||||
}
|
||||
field(LOPR,DBF_FLOAT) {
|
||||
prompt("Low Operating Range")
|
||||
promptgroup(GUI_DISPLAY)
|
||||
interest(1)
|
||||
}
|
||||
field(HIHI,DBF_FLOAT) {
|
||||
prompt("Hihi Alarm Limit")
|
||||
promptgroup(GUI_ALARMS)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(LOLO,DBF_FLOAT) {
|
||||
prompt("Lolo Alarm Limit")
|
||||
promptgroup(GUI_ALARMS)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(HIGH,DBF_FLOAT) {
|
||||
prompt("High Alarm Limit")
|
||||
promptgroup(GUI_ALARMS)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(LOW,DBF_FLOAT) {
|
||||
prompt("Low Alarm Limit")
|
||||
promptgroup(GUI_ALARMS)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(HHSV,DBF_MENU) {
|
||||
prompt("Hihi Severity")
|
||||
promptgroup(GUI_ALARMS)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(LLSV,DBF_MENU) {
|
||||
prompt("Lolo Severity")
|
||||
promptgroup(GUI_ALARMS)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(HSV,DBF_MENU) {
|
||||
prompt("High Severity")
|
||||
promptgroup(GUI_ALARMS)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(LSV,DBF_MENU) {
|
||||
prompt("Low Severity")
|
||||
promptgroup(GUI_ALARMS)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(HYST,DBF_DOUBLE) {
|
||||
prompt("Alarm Deadband")
|
||||
promptgroup(GUI_ALARMS)
|
||||
interest(1)
|
||||
}
|
||||
field(ADEL,DBF_DOUBLE) {
|
||||
prompt("Archive Deadband")
|
||||
promptgroup(GUI_DISPLAY)
|
||||
interest(1)
|
||||
}
|
||||
field(MDEL,DBF_DOUBLE) {
|
||||
prompt("Monitor Deadband")
|
||||
promptgroup(GUI_DISPLAY)
|
||||
interest(1)
|
||||
}
|
||||
field(LALM,DBF_DOUBLE) {
|
||||
prompt("Last Value Alarmed")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(ALST,DBF_DOUBLE) {
|
||||
prompt("Last Value Archived")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(MLST,DBF_DOUBLE) {
|
||||
prompt("Last Val Monitored")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
include "xxxRecord.dbd"
|
||||
device(xxx,CONSTANT,devXxxSoft,"SoftChannel")
|
||||
@@ -0,0 +1,6 @@
|
||||
TOP = ..
|
||||
include $(TOP)/configure/CONFIG
|
||||
DIRS += $(wildcard *ioc*)
|
||||
DIRS += $(wildcard as*)
|
||||
include $(CONFIG)/RULES_DIRS
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
TOP = ../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
ARCH = _ARCH_
|
||||
TARGETS = envPaths
|
||||
include $(TOP)/configure/RULES.ioc
|
||||
@@ -0,0 +1,5 @@
|
||||
TOP = ../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
ARCH = _ARCH_
|
||||
TARGETS = cdCommands
|
||||
include $(TOP)/configure/RULES.ioc
|
||||
@@ -0,0 +1,5 @@
|
||||
TOP = ../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
ARCH = _ARCH_
|
||||
TARGETS = envPaths dllPath.bat
|
||||
include $(TOP)/configure/RULES.ioc
|
||||
@@ -0,0 +1,9 @@
|
||||
To start the ioc from this directory execute the command
|
||||
../../bin/_ARCH_/<appname> st.cmd
|
||||
|
||||
Alternatively make the st.cmd file directly executable with
|
||||
chmod +x st.cmd
|
||||
and check the executable name on the first line of the st.cmd file
|
||||
|
||||
You may need to change the name of the .dbd file given in the
|
||||
st.cmd's dbLoadDatabase() command before starting the ioc.
|
||||
@@ -0,0 +1,6 @@
|
||||
Copy the startup script (st.cmd) and top level db and dbd directories and
|
||||
contents to
|
||||
<<TFTP_ROOT>>/epics/<<IOC hostname>>/
|
||||
|
||||
Then load the executable into the IOC (floppy disk, network boot, debugger,
|
||||
etc.) and start it.
|
||||
@@ -0,0 +1,28 @@
|
||||
#!../../bin/_ARCH_/_APPNAME_
|
||||
|
||||
## You may have to change _APPNAME_ to something else
|
||||
## everywhere it appears in this file
|
||||
|
||||
< envPaths
|
||||
|
||||
cd ${TOP}
|
||||
|
||||
## Register all support components
|
||||
dbLoadDatabase "dbd/_APPNAME_.dbd"
|
||||
_CSAFEAPPNAME__registerRecordDeviceDriver pdbbase
|
||||
|
||||
## Load record instances
|
||||
dbLoadTemplate "db/userHost.substitutions"
|
||||
dbLoadRecords "db/dbSubExample.db", "user=_USER_Host"
|
||||
|
||||
## Set this to see messages from mySub
|
||||
#var mySubDebug 1
|
||||
|
||||
## Run this to trace the stages of iocInit
|
||||
#traceIocInit
|
||||
|
||||
cd ${TOP}/iocBoot/${IOC}
|
||||
iocInit
|
||||
|
||||
## Start any sequence programs
|
||||
#seq sncExample, "user=_USER_Host"
|
||||
@@ -0,0 +1,25 @@
|
||||
## Example RTEMS startup script
|
||||
|
||||
## You may have to change _APPNAME_ to something else
|
||||
## everywhere it appears in this file
|
||||
|
||||
#< envPaths
|
||||
|
||||
## Register all support components
|
||||
dbLoadDatabase("dbd/_APPNAME_.dbd")
|
||||
_CSAFEAPPNAME__registerRecordDeviceDriver(pdbbase)
|
||||
|
||||
## Load record instances
|
||||
dbLoadTemplate("db/user.substitutions")
|
||||
dbLoadRecords("db/dbSubExample.db", "user=_USER_")
|
||||
|
||||
## Set this to see messages from mySub
|
||||
#var mySubDebug 1
|
||||
|
||||
## Run this to trace the stages of iocInit
|
||||
#traceIocInit
|
||||
|
||||
iocInit
|
||||
|
||||
## Start any sequence programs
|
||||
#seq(sncExample, "user=_USER_")
|
||||
@@ -0,0 +1,35 @@
|
||||
## Example vxWorks startup file
|
||||
|
||||
## The following is needed if your board support package doesn't at boot time
|
||||
## automatically cd to the directory containing its startup script
|
||||
#cd "_TOP_/iocBoot/_IOC_"
|
||||
|
||||
< cdCommands
|
||||
#< ../nfsCommands
|
||||
|
||||
cd topbin
|
||||
|
||||
## You may have to change _APPNAME_ to something else
|
||||
## everywhere it appears in this file
|
||||
ld 0,0, "_APPNAME_.munch"
|
||||
|
||||
## Register all support components
|
||||
cd top
|
||||
dbLoadDatabase "dbd/_APPNAME_.dbd"
|
||||
_CSAFEAPPNAME__registerRecordDeviceDriver pdbbase
|
||||
|
||||
## Load record instances
|
||||
dbLoadTemplate "db/user.substitutions"
|
||||
dbLoadRecords "db/dbSubExample.db", "user=_USER_"
|
||||
|
||||
## Set this to see messages from mySub
|
||||
#mySubDebug = 1
|
||||
|
||||
## Run this to trace the stages of iocInit
|
||||
#traceIocInit
|
||||
|
||||
cd startup
|
||||
iocInit
|
||||
|
||||
## Start any sequence programs
|
||||
#seq &sncExample, "user=_USER_"
|
||||
@@ -0,0 +1,26 @@
|
||||
#Instructions for creating and using a real nfsCommands file
|
||||
#
|
||||
# in order to use nfs do the following:
|
||||
# 1) Create hostAdd and nfsMount commands for each nfs server
|
||||
# 2) In each st.cmd file add the following two commands BEFORE any load commands
|
||||
# ../nfs.cmd
|
||||
# cd "<iocname>
|
||||
#
|
||||
# The hostAdd command has the form:
|
||||
# hostAdd("<host>","xxx.xxx.xxx.xxx")
|
||||
#
|
||||
# You can also mount subdirectories as follows:
|
||||
# nfsMount("<host>", "/xxx/xxx/xxx", "/xxx")
|
||||
#
|
||||
# For example assume
|
||||
#
|
||||
# host is mercury with inet address 155.77.2.56
|
||||
# You want to mount the directory (which is a file system of mercury)
|
||||
# /home/mercury5/iocinfo
|
||||
# as
|
||||
# /iocinfo
|
||||
#
|
||||
# The commands would be
|
||||
#
|
||||
# hostAdd("mercury","155.77.2.56")
|
||||
# nfsMount("mercury","/home/mercury5/iocinfo","/iocinfo")
|
||||
@@ -0,0 +1,29 @@
|
||||
#Instructions for creating and using a real nfsCommands file
|
||||
#
|
||||
# in order to use nfs do the following:
|
||||
# 1) Create hostAdd and nfsMount commands for each nfs server
|
||||
# 2) In each st.cmd file add the following two commands BEFORE any load commands
|
||||
# ../nfs.cmd
|
||||
# cd "<iocname>
|
||||
#
|
||||
# The hostAdd command has the form:
|
||||
# hostAdd("<host>","xxx.xxx.xxx.xxx")
|
||||
#
|
||||
# The nfsMount command has the form:
|
||||
# nfsMount("<host>", "/xxx/xxx/xxx", "/xxx")
|
||||
#
|
||||
# You can also mount subdirectories as follows:
|
||||
# nfsMountAll("<host>")
|
||||
#
|
||||
# For example assume
|
||||
#
|
||||
# host is mercury with inet address 155.77.2.56
|
||||
# You want to mount the directory (which is a file system of mercury)
|
||||
# /home/mercury5/iocinfo
|
||||
# as
|
||||
# /iocinfo
|
||||
#
|
||||
# The commands would be
|
||||
#
|
||||
# hostAdd("mercury","155.77.2.56")
|
||||
# nfsMount("mercury","/home/mercury5/iocinfo","/iocinfo")
|
||||
@@ -0,0 +1,22 @@
|
||||
TOP=../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
#----------------------------------------
|
||||
# ADD MACRO DEFINITIONS AFTER THIS LINE
|
||||
|
||||
#----------------------------------------------------
|
||||
# Optimization of db files using dbst (DEFAULT: NO)
|
||||
#DB_OPT = YES
|
||||
|
||||
#----------------------------------------------------
|
||||
# Create and install (or just install) into <top>/db
|
||||
# databases, templates, substitutions like this
|
||||
#DB += xxx.db
|
||||
|
||||
#----------------------------------------------------
|
||||
# If <anyname>.db template is not named <anyname>*.template add
|
||||
# <anyname>_template = <templatename>
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
#----------------------------------------
|
||||
# ADD RULES AFTER THIS LINE
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
TOP = ..
|
||||
include $(TOP)/configure/CONFIG
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *src*))
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Src*))
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *db*))
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Db*))
|
||||
include $(TOP)/configure/RULES_DIRS
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
TOP=../..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
#----------------------------------------
|
||||
# ADD MACRO DEFINITIONS AFTER THIS LINE
|
||||
#=============================
|
||||
|
||||
#=============================
|
||||
# Build the IOC application
|
||||
|
||||
PROD_IOC = _APPNAME_
|
||||
# _APPNAME_.dbd will be created and installed
|
||||
DBD += _APPNAME_.dbd
|
||||
|
||||
# _APPNAME_.dbd will be made up from these files:
|
||||
_APPNAME__DBD += base.dbd
|
||||
|
||||
# Include dbd files from all support applications:
|
||||
#_APPNAME__DBD += xxx.dbd
|
||||
|
||||
# Add all the support libraries needed by this IOC
|
||||
#_APPNAME__LIBS += xxx
|
||||
|
||||
# _APPNAME__registerRecordDeviceDriver.cpp derives from _APPNAME_.dbd
|
||||
_APPNAME__SRCS += _APPNAME__registerRecordDeviceDriver.cpp
|
||||
|
||||
# Build the main IOC entry point on workstation OSs.
|
||||
_APPNAME__SRCS_DEFAULT += _APPNAME_Main.cpp
|
||||
_APPNAME__SRCS_vxWorks += -nil-
|
||||
|
||||
# Add support from base/src/vxWorks if needed
|
||||
#_APPNAME__OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary
|
||||
|
||||
# Finally link to the EPICS Base libraries
|
||||
_APPNAME__LIBS += $(EPICS_BASE_IOC_LIBS)
|
||||
|
||||
#===========================
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
#----------------------------------------
|
||||
# ADD RULES AFTER THIS LINE
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/* _APPNAME_Main.cpp */
|
||||
/* Author: Marty Kraimer Date: 17MAR2000 */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "epicsExit.h"
|
||||
#include "epicsThread.h"
|
||||
#include "iocsh.h"
|
||||
|
||||
int main(int argc,char *argv[])
|
||||
{
|
||||
if(argc>=2) {
|
||||
iocsh(argv[1]);
|
||||
epicsThreadSleep(.2);
|
||||
}
|
||||
iocsh(NULL);
|
||||
epicsExit(0);
|
||||
return(0);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
TOP = ..
|
||||
include $(TOP)/configure/CONFIG
|
||||
DIRS += $(wildcard *ioc*)
|
||||
DIRS += $(wildcard as*)
|
||||
include $(CONFIG)/RULES_DIRS
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
TOP = ../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
ARCH = _ARCH_
|
||||
TARGETS = envPaths
|
||||
include $(TOP)/configure/RULES.ioc
|
||||
@@ -0,0 +1,5 @@
|
||||
TOP = ../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
ARCH = _ARCH_
|
||||
TARGETS = cdCommands
|
||||
include $(TOP)/configure/RULES.ioc
|
||||
@@ -0,0 +1,5 @@
|
||||
TOP = ../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
ARCH = _ARCH_
|
||||
TARGETS = envPaths dllPath.bat
|
||||
include $(TOP)/configure/RULES.ioc
|
||||
@@ -0,0 +1,21 @@
|
||||
#!../../bin/_ARCH_/_APPNAME_
|
||||
|
||||
## You may have to change _APPNAME_ to something else
|
||||
## everywhere it appears in this file
|
||||
|
||||
< envPaths
|
||||
|
||||
cd ${TOP}
|
||||
|
||||
## Register all support components
|
||||
dbLoadDatabase "dbd/_APPNAME_.dbd"
|
||||
_CSAFEAPPNAME__registerRecordDeviceDriver pdbbase
|
||||
|
||||
## Load record instances
|
||||
#dbLoadRecords("db/xxx.db","user=_USER_Host")
|
||||
|
||||
cd ${TOP}/iocBoot/${IOC}
|
||||
iocInit
|
||||
|
||||
## Start any sequence programs
|
||||
#seq sncxxx,"user=_USER_Host"
|
||||
@@ -0,0 +1,18 @@
|
||||
#!../../bin/_ARCH_/_APPNAME_
|
||||
|
||||
## You may have to change _APPNAME_ to something else
|
||||
## everywhere it appears in this file
|
||||
|
||||
#< envPaths
|
||||
|
||||
## Register all support components
|
||||
dbLoadDatabase("../../dbd/_APPNAME_.dbd",0,0)
|
||||
_CSAFEAPPNAME__registerRecordDeviceDriver(pdbbase)
|
||||
|
||||
## Load record instances
|
||||
dbLoadRecords("../../db/_APPNAME_.db","user=_USER_")
|
||||
|
||||
iocInit()
|
||||
|
||||
## Start any sequence programs
|
||||
#seq snc_APPNAME_,"user=_USER_"
|
||||
@@ -0,0 +1,19 @@
|
||||
## Example RTEMS startup script
|
||||
|
||||
## You may have to change _APPNAME_ to something else
|
||||
## everywhere it appears in this file
|
||||
|
||||
#< envPaths
|
||||
|
||||
## Register all support components
|
||||
dbLoadDatabase("dbd/_APPNAME_.dbd")
|
||||
_CSAFEAPPNAME__registerRecordDeviceDriver(pdbbase)
|
||||
|
||||
## Load record instances
|
||||
#dbLoadTemplate("db/_APPNAME_.substitutions")
|
||||
#dbLoadRecords("db/_APPNAME_.db", "user=_USER_")
|
||||
|
||||
iocInit
|
||||
|
||||
## Start any sequence programs
|
||||
#seq(sncxxx, "user=_USER_")
|
||||
@@ -0,0 +1,29 @@
|
||||
## Example vxWorks startup file
|
||||
|
||||
## The following is needed if your board support package doesn't at boot time
|
||||
## automatically cd to the directory containing its startup script
|
||||
#cd "_TOP_/iocBoot/_IOC_"
|
||||
|
||||
< cdCommands
|
||||
#< ../nfsCommands
|
||||
|
||||
cd topbin
|
||||
|
||||
## You may have to change _APPNAME_ to something else
|
||||
## everywhere it appears in this file
|
||||
ld 0,0, "_APPNAME_.munch"
|
||||
|
||||
## Register all support components
|
||||
cd top
|
||||
dbLoadDatabase "dbd/_APPNAME_.dbd"
|
||||
_CSAFEAPPNAME__registerRecordDeviceDriver pdbbase
|
||||
|
||||
## Load record instances
|
||||
#dbLoadTemplate "db/_APPNAME_.substitutions"
|
||||
#dbLoadRecords "db/_APPNAME_.db", "user=_USER_"
|
||||
|
||||
cd startup
|
||||
iocInit
|
||||
|
||||
## Start any sequence programs
|
||||
#seq &sncxxx, "user=_USER_"
|
||||
@@ -0,0 +1,29 @@
|
||||
#Instructions for creating and using a real nfsCommands file
|
||||
#
|
||||
# in order to use nfs do the following:
|
||||
# 1) Create hostAdd and nfsMount commands for each nfs server
|
||||
# 2) In each st.cmd file add the following two commands BEFORE any load commands
|
||||
# ../nfs.cmd
|
||||
# cd "<iocname>
|
||||
#
|
||||
# The hostAdd command has the form:
|
||||
# hostAdd("<host>","xxx.xxx.xxx.xxx")
|
||||
#
|
||||
# The vxWorks nfsMount command has the form:
|
||||
# nfsMount("<host>")
|
||||
#
|
||||
# You can also mount subdirectories as follows:
|
||||
# nfsMount("<host>", "/xxx/xxx/xxx", "/xxx")
|
||||
#
|
||||
# For example assume
|
||||
#
|
||||
# host is mercury with inet address 155.77.2.56
|
||||
# You want to mount the directory (which is a file system of mercury)
|
||||
# /home/mercury5/iocinfo
|
||||
# as
|
||||
# /iocinfo
|
||||
#
|
||||
# The commands would be
|
||||
#
|
||||
# hostAdd("mercury","155.77.2.56")
|
||||
# nfsMount("mercury","/home/mercury5/iocinfo","/iocinfo")
|
||||
@@ -0,0 +1,29 @@
|
||||
#Instructions for creating and using a real nfsCommands file
|
||||
#
|
||||
# in order to use nfs do the following:
|
||||
# 1) Create hostAdd and nfsMount commands for each nfs server
|
||||
# 2) In each st.cmd file add the following two commands BEFORE any load commands
|
||||
# ../nfs.cmd
|
||||
# cd "<iocname>
|
||||
#
|
||||
# The hostAdd command has the form:
|
||||
# hostAdd("<host>","xxx.xxx.xxx.xxx")
|
||||
#
|
||||
# The vxWorks nfsMount command has the form:
|
||||
# nfsMount("<host>")
|
||||
#
|
||||
# You can also mount subdirectories as follows:
|
||||
# nfsMount("<host>", "/xxx/xxx/xxx", "/xxx")
|
||||
#
|
||||
# For example assume
|
||||
#
|
||||
# host is mercury with inet address 155.77.2.56
|
||||
# You want to mount the directory (which is a file system of mercury)
|
||||
# /home/mercury5/iocinfo
|
||||
# as
|
||||
# /iocinfo
|
||||
#
|
||||
# The commands would be
|
||||
#
|
||||
# hostAdd("mercury","155.77.2.56")
|
||||
# nfsMount("mercury","/home/mercury5/iocinfo","/iocinfo")
|
||||
@@ -0,0 +1,22 @@
|
||||
TOP=../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
#----------------------------------------
|
||||
# ADD MACRO DEFINITIONS AFTER THIS LINE
|
||||
|
||||
#----------------------------------------------------
|
||||
# Optimization of db files using dbst (DEFAULT: NO)
|
||||
#DB_OPT = YES
|
||||
|
||||
#----------------------------------------------------
|
||||
# Create and install (or just install) into <top>/db
|
||||
# databases, templates, substitutions like this
|
||||
#DB += xxx.db
|
||||
|
||||
#----------------------------------------------------
|
||||
# If <anyname>.db template is not named <anyname>*.template add
|
||||
# <anyname>_template = <templatename>
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
#----------------------------------------
|
||||
# ADD RULES AFTER THIS LINE
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
TOP = ..
|
||||
include $(TOP)/configure/CONFIG
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *src*))
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Src*))
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *db*))
|
||||
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Db*))
|
||||
include $(TOP)/configure/RULES_DIRS
|
||||
@@ -0,0 +1,28 @@
|
||||
TOP=../..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
#----------------------------------------
|
||||
# ADD MACRO DEFINITIONS AFTER THIS LINE
|
||||
#=============================
|
||||
|
||||
#==================================================
|
||||
# build a support library
|
||||
|
||||
LIBRARY_IOC += _APPNAME_
|
||||
|
||||
# xxxRecord.h will be created from xxxRecord.dbd
|
||||
#DBDINC += xxxRecord
|
||||
# install _APPNAME_.dbd into <top>/dbd
|
||||
DBD += _APPNAME_.dbd
|
||||
|
||||
# specify all source files to be compiled and added to the library
|
||||
#_APPNAME__SRCS += xxx
|
||||
|
||||
_APPNAME__LIBS += $(EPICS_BASE_IOC_LIBS)
|
||||
|
||||
#===========================
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
#----------------------------------------
|
||||
# ADD RULES AFTER THIS LINE
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# provide definitions such as
|
||||
#include "xxxRecord.dbd"
|
||||
#device(xxx,CONSTANT,devXxxSoft,"SoftChannel")
|
||||
#driver(myDriver)
|
||||
#registrar(myRegistrar)
|
||||
#variable(myVariable)
|
||||
Reference in New Issue
Block a user