Copy of old lessons from afs webpage
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
# Example EPICS Makefile
|
||||
|
||||
# If you don't modify this file it will create
|
||||
# a program with the name of the current directory
|
||||
# from all C and C++ source files found and link
|
||||
# it to the EPICS client libraries.
|
||||
|
||||
# Where is EPICS base?
|
||||
EPICS = /usr/local/epics/base
|
||||
|
||||
# Where to install the program)?
|
||||
BINDIR = .
|
||||
#BINDIR = bin/$(EPICS_HOST_ARCH)
|
||||
|
||||
# What is the name of the program?
|
||||
# Add one line for each program if the program name
|
||||
# is not equal to the directory name
|
||||
PROGRAM +=
|
||||
|
||||
# List all sources of the program if not simply
|
||||
# all *.c *.cc *.C *.cxx *.cpp files in this
|
||||
# directory should be used.
|
||||
# Add one line for each source file.
|
||||
# If you build more than one PROGRAM, list
|
||||
# the sources separately for each program like
|
||||
# SRCS_<program> += <filename>
|
||||
SRCS +=
|
||||
|
||||
# list all include directories
|
||||
INCDIRS += $(EPICS)/include/os/Linux
|
||||
INCDIRS += $(EPICS)/include
|
||||
|
||||
# list all library directories
|
||||
LIBDIRS += $(EPICS)/lib/$(EPICS_HOST_ARCH)
|
||||
|
||||
# list all libraries (ca and Com are EPICS)
|
||||
LIBS += ca Com
|
||||
|
||||
#optimize:
|
||||
CFLAGS += -O3
|
||||
#debug:
|
||||
CFLAGS += -g
|
||||
|
||||
# don't touch the code below this line unless you know what you're doing.
|
||||
CPPFLAGS += $(INCDIRS:%=-I %)
|
||||
|
||||
CFLAGS += -MMD
|
||||
CFLAGS += -Wall
|
||||
CFLAGS += $(USR_CFLAGS)
|
||||
|
||||
LDFLAGS += $(LIBDIRS:%=-L %)
|
||||
LDFLAGS += $(LIBDIRS:%=-Wl,-rpath,%)
|
||||
LDFLAGS += $(LIBS:%=-l %)
|
||||
|
||||
ifeq ($(words $(PROGRAM)),0)
|
||||
PROGRAM = $(notdir $(PWD))
|
||||
endif
|
||||
|
||||
SRCS += $(SRCS_$(PROGRAM))
|
||||
ifeq ($(words $(SRCS)),0)
|
||||
SRCS += $(wildcard *.c)
|
||||
SRCS += $(wildcard *.cc)
|
||||
SRCS += $(wildcard *.C)
|
||||
SRCS += $(wildcard *.cxx)
|
||||
SRCS += $(wildcard *.cpp)
|
||||
endif
|
||||
|
||||
OBJS = $(addprefix O.$(EPICS_HOST_ARCH)/,$(addsuffix .o,$(basename $(SRCS))))
|
||||
|
||||
ifndef EPICS_HOST_ARCH
|
||||
$(error EPICS_HOST_ARCH variable is missing on your system!)
|
||||
endif
|
||||
|
||||
.PHONY:
|
||||
.PHONY: build clean realclean
|
||||
build:
|
||||
|
||||
clean:
|
||||
rm -rf O.*
|
||||
|
||||
realclean: clean
|
||||
rm -f $(foreach prog,$(PROGRAM),$(BINDIR)/$(prog))
|
||||
|
||||
O.%:
|
||||
mkdir $@
|
||||
|
||||
$(BINDIR):
|
||||
mkdir -p $@
|
||||
|
||||
ifeq ($(words $(PROGRAM)),1)
|
||||
|
||||
build: $(BINDIR)/$(PROGRAM)
|
||||
|
||||
ifneq ($(BINDIR),.)
|
||||
$(PROGRAM): $(BINDIR)/$(PROGRAM)
|
||||
endif
|
||||
|
||||
$(BINDIR)/$(PROGRAM): $(BINDIR) O.$(EPICS_HOST_ARCH) O.$(EPICS_HOST_ARCH)/$(PROGRAM)
|
||||
rm -f $@
|
||||
cp O.$(EPICS_HOST_ARCH)/$(@F) $@
|
||||
|
||||
O.$(EPICS_HOST_ARCH)/$(PROGRAM): $(OBJS)
|
||||
$(CXX) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
else
|
||||
|
||||
build:
|
||||
for prog in $(PROGRAM); do make PROGRAM=$$prog; done
|
||||
|
||||
$(PROGRAM):
|
||||
make PROGRAM=$@
|
||||
|
||||
endif
|
||||
|
||||
O.$(EPICS_HOST_ARCH)/%.o: %.c
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@
|
||||
|
||||
O.$(EPICS_HOST_ARCH)/%.o: %.cc
|
||||
$(CXX) -c $(CPPFLAGS) $(CFLAGS) $(CXXFLAGS) $< -o $@
|
||||
|
||||
O.$(EPICS_HOST_ARCH)/%.o: %.C
|
||||
$(CXX) -c $(CPPFLAGS) $(CFLAGS) $(CXXFLAGS) $< -o $@
|
||||
|
||||
O.$(EPICS_HOST_ARCH)/%.o: %.cxx
|
||||
$(CXX) -c $(CPPFLAGS) $(CFLAGS) $(CXXFLAGS) $< -o $@
|
||||
|
||||
O.$(EPICS_HOST_ARCH)/%.o: %.cpp
|
||||
$(CXX) -c $(CPPFLAGS) $(CFLAGS) $(CXXFLAGS) $< -o $@
|
||||
|
||||
-include O.$(EPICS_HOST_ARCH)/*.d
|
||||
@@ -0,0 +1,171 @@
|
||||
/* caLesson1.c
|
||||
by Dirk Zimoch, 2007
|
||||
|
||||
This is a very simple channel access client program.
|
||||
It uses the EPICS R3.13 channel access functions
|
||||
but can as well run with EPICS 3.14.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* include EPICS headers */
|
||||
#include <cadef.h>
|
||||
|
||||
/*
|
||||
#define WITH_NOT_EXISTING_CHANNEL
|
||||
*/
|
||||
|
||||
/* Strings describing the connection status of a channel */
|
||||
const char *channel_state_str[4] = {
|
||||
"not found",
|
||||
"connection lost",
|
||||
"connected",
|
||||
"closed"
|
||||
};
|
||||
|
||||
int main ()
|
||||
{
|
||||
chid beamcurrentID;
|
||||
double beamcurrent;
|
||||
|
||||
chid gapID;
|
||||
double gap;
|
||||
|
||||
#ifdef WITH_NOT_EXISTING_CHANNEL
|
||||
chid doesnotexistID;
|
||||
double doesnotexist;
|
||||
#endif
|
||||
|
||||
int status;
|
||||
|
||||
|
||||
/* Step1: initialize channel access and search for all channels. */
|
||||
ca_task_initialize();
|
||||
|
||||
/* Assign channel names to channel IDs. */
|
||||
ca_search ("ARIDI-PCT:CURRENT", &beamcurrentID);
|
||||
#ifdef WITH_NOT_EXISTING_CHANNEL
|
||||
ca_search ("doesnotexist", &doesnotexistID);
|
||||
#endif
|
||||
ca_search ("X10SA-ID-GAP:READ", &gapID);
|
||||
/* Nothing has been sent to the network so far! */
|
||||
|
||||
/* Send all requests in parallel, wait for maximal 5.0 seconds. */
|
||||
printf ("searching ...\n");
|
||||
status = ca_pend_io(5.0);
|
||||
|
||||
/* This ca_pend_io() is a very expensive action in terms of network
|
||||
bandwidth because UDP broadcasts are sent to all IOCs in the subnet.
|
||||
|
||||
For every broadcast, each IOC has to check if it owns one of the
|
||||
requested channels. If no IOC replies, the boradcast request
|
||||
is repeated up to 100 times.
|
||||
Do not search for obsolete channels! If channels have been
|
||||
removed, also remove them from your clients to reduce unnecessary
|
||||
broadcast traffic. Check the spelling of channel names if
|
||||
channels don't connect.
|
||||
|
||||
One broadcast package can contain many channel requests. This
|
||||
is much more efficient than sending only one request at a time.
|
||||
Thus, always try to connect all channels at once, using only
|
||||
one ca_pend_io() after all ca_search() calls. This also speeds up
|
||||
your program: waiting 10 seconds for 1000 channels in parallel
|
||||
is much shorter than even waiting only 1 second for 1000 sequential
|
||||
channel searches. ca_pend_io() returns early when all channels are
|
||||
found.
|
||||
*/
|
||||
|
||||
/* Check for errors */
|
||||
switch (status)
|
||||
{
|
||||
case ECA_NORMAL:
|
||||
printf ("all channels found\n");
|
||||
break;
|
||||
case ECA_TIMEOUT:
|
||||
printf ("some channels not found yet\n");
|
||||
break;
|
||||
default:
|
||||
printf ("unexpected error while searching: %s\n",
|
||||
ca_message(status));
|
||||
}
|
||||
/* If not all channels can be found now, the IOC is probably down.
|
||||
Searching continues in the background and channels connect
|
||||
automatically when the IOC comes up.
|
||||
|
||||
Try to uncomment the #define WITH_NOT_EXISTING_CHANNEL above to
|
||||
see what happens if a channel cannot be found.
|
||||
|
||||
Normally, ca_search() should not be called any more after startup.
|
||||
There may be exceptions, when channels are added dynamically to a
|
||||
running program. But this is not the normal case.
|
||||
|
||||
Connected channels may disconnect and reconnect later automatically
|
||||
when an IOC reboots. Always keep this in mind when doing any
|
||||
network traffic. Any long-lived program, such as GUIs or servers,
|
||||
MUST be written in a way to survive disconnected channels and
|
||||
they MUST react in a reasonable manner.
|
||||
|
||||
It depends on the application and is generally is your problem what
|
||||
"reasonable" means.
|
||||
*/
|
||||
|
||||
/* Step 2: do channel access data transfer. */
|
||||
ca_get(DBR_DOUBLE, beamcurrentID, &beamcurrent);
|
||||
#ifdef WITH_NOT_EXISTING_CHANNEL
|
||||
ca_get(DBR_DOUBLE, doesnotexistID, &doesnotexist);
|
||||
#endif
|
||||
ca_get(DBR_DOUBLE, gapID, &gap);
|
||||
/* Nothing has been sent to the network so far! */
|
||||
|
||||
/* Send all request in parallel, wait for maximal 1.0 second. */
|
||||
printf ("reading ...\n");
|
||||
status = ca_pend_io(1.0);
|
||||
|
||||
/* As before, it increases network performance to do as many ca_get()
|
||||
calls as possible with one ca_pend_io(). In opposite to searching,
|
||||
data transfer is done via TCP. Thus, it affects only the client
|
||||
and the IOC and all network components in between. It does not
|
||||
affect all IOCs on the same network as searching does! But still,
|
||||
many requests can be sent in the same message if they go to the
|
||||
same IOC -- which is often the case. Luckily, you don't have to
|
||||
care about this. Just always try to read as many channels as
|
||||
possible in parallel.
|
||||
*/
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case ECA_NORMAL:
|
||||
printf ("all values received\n");
|
||||
break;
|
||||
case ECA_TIMEOUT:
|
||||
printf ("some values not received\n");
|
||||
break;
|
||||
default:
|
||||
printf ("unexpected error while reading: %s\n",
|
||||
ca_message(status));
|
||||
}
|
||||
|
||||
/* Print values of all channels but inform the user
|
||||
if a channel is connected or not. The value of a
|
||||
not connected channel is not valid, of course.
|
||||
Never take such a value for serious!
|
||||
Always when the result of ca_pend_io() after ca_get()
|
||||
is not ECA_NORMAL, you MUST check ca_state() of all
|
||||
involved channels before trusting any value.
|
||||
*/
|
||||
printf ("Beam current (%s): %g\n",
|
||||
channel_state_str[ca_state(beamcurrentID)],
|
||||
beamcurrent);
|
||||
#ifdef WITH_NOT_EXISTING_CHANNEL
|
||||
printf ("Does not exist (%s): %g\n",
|
||||
channel_state_str[ca_state(doesnotexistID)],
|
||||
doesnotexist);
|
||||
#endif
|
||||
printf ("Gap (%s): %g\n",
|
||||
channel_state_str[ca_state(gapID)],
|
||||
gap);
|
||||
|
||||
/* Last step: free all channel access resources */
|
||||
ca_task_exit();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user